@indiegems/gem-web-sdk 13.4.1 → 13.4.3
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 +12 -0
- package/dist/gem-web-sdk.esm.js +1 -1
- package/dist/gem-web-sdk.function.js +1 -1
- 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 +1 -1
- package/docs/api/api-reference-function.md +1 -1
- package/docs/api/api-reference-game.md +13 -12
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +2 -2
- package/docs/changelog.md +12 -0
- package/docs/getting-started.md +7 -14
- package/docs/manifest.json +8 -8
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +29 -23
- package/package.json +1 -1
- package/schema/bridge.v1.json +2 -2
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var Ce=Object.defineProperty;var Ie=(n,e)=>{for(var t in e)Ce(n,t,{get:e[t],enumerable:true});};globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:13.4.1]";var Ee={initialDelayMs:2e3,maxDelayMs:6e4};function Me(n,e){return e!==void 0?!e:n==="chain_exhausted"}var B=class{constructor(e){this.options=e;}options;grant=null;state="pending";failure=null;generation=0;inFlight=null;rejected=false;retry=null;failures=0;notBefore=0;disposed=false;get currentState(){return this.state}get lastFailure(){return this.failure}async get(){if(this.state==="lost")throw new _(this.failure??"unavailable","the session cannot be renewed");let e=this.seconds();if(this.grant&&e<this.grant.expiresAt-this.renewBefore())return this.grant.token;if(this.pastChainCeiling())throw new _("chain_exhausted","the token chain reached its ceiling");if(this.inFlight===null&&this.options.clock.now()<this.notBefore)throw new _(this.failure??"unavailable","the embedder asked for no new token yet; one is asked for when that wait is over");return this.refresh(this.nextReason())}pastChainCeiling(){return this.grant===null||this.seconds()<this.grant.chainEndsAt?false:(this.enter("lost","chain_exhausted"),true)}nextReason(){return this.rejected?"rejected":this.grant?"expiring":"initial"}invalidate(e){e!==void 0&&this.grant!==null&&this.grant.token!==e||this.rejected&&this.grant===null||(this.grant=null,this.generation+=1,this.inFlight=null,this.rejected=true,this.state!=="lost"&&this.enter("pending"));}accept(e){this.state!=="lost"&&(this.grant=e,this.rejected=false,this.enter("ready"));}close(e="unavailable"){this.grant=null,this.generation+=1,this.inFlight=null,this.enter("lost",e);}dispose(){this.disposed=true,this.cancelRetry();}scheduleRetry(e){if(this.cancelRetry(),this.failures+=1,this.notBefore=e===void 0?0:this.options.clock.now()+e,this.disposed)return;let{initialDelayMs:t,maxDelayMs:r}=Ee,o=Math.min(r,t*2**(this.failures-1)),a=Math.max(o/2,e??0)+Math.random()*(o/2);this.retry={handle:this.options.clock.setTimeout(()=>this.retryNow(),a)};}retryNow(){this.retry=null,this.notBefore=0,!(this.state==="lost"||this.pastChainCeiling())&&this.refresh(this.nextReason()).catch(()=>{});}cancelRetry(){this.retry!==null&&(this.options.clock.clearTimeout(this.retry.handle),this.retry=null);}refresh(e){if(this.inFlight)return this.inFlight;this.cancelRetry();let t=this.generation;this.enter("pending");let r=this.options.request(e).then(o=>{if(t!==this.generation)throw new _("internal","token arrived for a superseded generation");return this.grant=o,this.rejected=false,this.enter("ready"),o.token}).catch(o=>{let a=Le(o),{retryable:i,retryAfterMs:l}=o??{};throw t===this.generation&&(Me(a,typeof i=="boolean"?i:void 0)?this.enter("lost",a):(this.scheduleRetry(typeof l=="number"&&Number.isFinite(l)&&l>0?l:void 0),this.enter("degraded",a))),o instanceof _?o:new _(a,String(o))}).finally(()=>{this.inFlight===r&&(this.inFlight=null);});return this.inFlight=r,r}enter(e,t){(e==="ready"||e==="lost")&&this.cancelRetry(),e==="ready"&&(this.failures=0,this.notBefore=0),this.failure=t??(e==="ready"?null:this.failure),this.state!==e&&(this.state=e,this.options.onStateChange?.(e,t));}renewBefore(){return this.options.renewBeforeSeconds??60}seconds(){return Math.floor(this.options.clock.now()/1e3)}},_=class n extends Error{constructor(t,r){super(`auth ${t}: ${r}`);this.reason=t;this.name="AuthError";}reason;static isAuthError(t){if(t instanceof n)return true;let r=t;return r?.name==="AuthError"&&typeof r.reason=="string"}};function Le(n){if(n instanceof _)return n.reason;let e=n?.reason;return typeof e=="string"&&["chain_exhausted","unavailable","timeout","internal"].includes(e)?e:"internal"}var y=class n extends Error{retryable=false;constructor(e){super(e),this.name="TransportRefusal";}static isRefusal(e){if(e instanceof n)return true;let t=e;return t?.name==="TransportRefusal"&&t.retryable===false}},F=["if-match","if-none-match"],Y=4096;function qe(n){if(!n)return;let e={};for(let[t,r]of Object.entries(n)){let o=t.toLowerCase();if(!F.includes(o)||r===void 0)continue;let a=typeof r=="string"?$e(r):r;if(typeof a!="string"||a.length>Y||!je(a))throw new y(`the ${o} header value cannot be sent \u2014 it is not a string, is longer than ${Y} characters, or contains a character outside the range an HTTP header value may carry. This is a malformed request, not a server failure; the value is withheld here rather than written to logs.`);a!==""&&(e[o]=a);}return Object.freeze(e)}function $e(n){let e=o=>o===32||o===9||o===10||o===13,t=0,r=n.length;for(;t<r&&e(n.charCodeAt(t));)t++;for(;r>t&&e(n.charCodeAt(r-1));)r--;return n.slice(t,r)}function je(n){for(let e=0;e<n.length;e++){let t=n.charCodeAt(e);if(t!==9&&(t<32||t===127||t>255))return false}return true}function Be(n){let{bytes:e}=n;if(e!==void 0){if(Object.prototype.toString.call(e)!=="[object Uint8Array]")throw new y("the raw body is not a Uint8Array, so it cannot be sent as bytes. This is a malformed request, not a server failure.");if(n.body!==void 0)throw new y("the request carries both a JSON body and a raw body, and only one can be sent. This is a malformed request, not a server failure.");return new Uint8Array(e)}}function J(n,e){let t=e.replayUnauthorizedOnce??true;return {async request(r){let o={...r},a=qe(o.headers),i=Be(o),l=Object.freeze({...o,headers:a,...i===void 0?{}:{bytes:i}}),d=await e.tokenSource.get(),c=await n(l,d);if(c.status!==401||!t)return c;e.tokenSource.invalidate(d);let g=await e.tokenSource.get(),h=await n(l,g);if(h.status===401)throw e.tokenSource.close("unavailable"),new _("unavailable","the server refused a freshly minted token");return h}}}function X(n){let e=typeof n=="string"?new TextEncoder().encode(n):n;return btoa(Fe(e))}function Fe(n){let t="";for(let r=0;r<n.length;r+=32768)t+=String.fromCharCode(...n.subarray(r,r+32768));return t}var m=class extends Error{constructor(t,r,o,a,i){super(`${t} (${r}): ${o}`);this.reason=t;this.status=r;this.detail=o;this.retryAfterSeconds=a;this.code=i;this.name="GemApiError";}reason;status;detail;retryAfterSeconds;code;get retryable(){return this.reason==="unavailable"||this.reason==="rate_limited"}},Z=new WeakMap;function ee(n){return Z.get(n)??{}}var Ue=new Set([408,429,500,502,503,504]),Ne=6e4;function De(n){if(n===void 0)return 2;if(!Number.isInteger(n)||n<0||n>10)throw new y(`maxRetries must be a whole number between 0 and 10; received ${JSON.stringify(n)}. This is a configuration fault, not a server failure.`);return n}var C=class{constructor(e){this.options=e;}options;async get(e,t){return this.send({method:"GET",path:e,signal:t},true)}async getWithEtag(e,t={}){let r=await this.sendRaw({method:"GET",path:e,...t.signal===void 0?{}:{signal:t.signal},...t.ifNoneMatch===void 0?{}:{headers:{"if-none-match":t.ifNoneMatch}}},true),o=r.etag===void 0?{}:{etag:r.etag};return r.status===304?{modified:false,...o}:{modified:true,body:r.body,...o}}async put(e,t,r,o={}){return this.send({method:"PUT",path:e,body:t,signal:r,...L(o)},o.replaySafe===true,o.retryRateLimited!==false)}async post(e,t,r,o={}){return this.send({method:"POST",path:e,body:t,signal:r,...L(o)},o.replaySafe===true,o.retryRateLimited!==false)}async postBytes(e,t,r,o={}){let a=await this.sendRaw({method:"POST",path:e,bytes:t,responseType:"bytes",signal:r,...L(o)},o.replaySafe===true,o.retryRateLimited!==false);return {bytes:He(a.body),...a.invocationId===void 0?{}:{invocationId:a.invocationId}}}async patch(e,t,r,o={}){return this.send({method:"PATCH",path:e,body:t,signal:r,...L(o)},o.replaySafe===true,o.retryRateLimited!==false)}async delete(e,t,r={}){return this.send({method:"DELETE",path:e,signal:t,...L(r)},r.replaySafe===true,r.retryRateLimited!==false)}async send(e,t,r=true){return (await this.sendRaw(e,t,r)).body}async sendRaw(e,t,r=true){let o=t?De(this.options.maxRetries)+1:1,a=null;if(e.signal?.aborted)throw Q(e.signal);for(let i=0;i<o;i++){if(i>0&&await this.backoff(i,a,e.signal),e.signal?.aborted)throw Q(e.signal);let l;try{l=await this.options.transport.request(e);}catch(c){if(_.isAuthError(c)||y.isRefusal(c)||e.signal?.aborted||Ve(c))throw c;a=new m("unavailable",0,String(c));continue}if(l.status>=200&&l.status<300||l.status===304)return l;let d=We(l);if(!Ue.has(l.status)||l.status===429&&!r)throw d;a=d;}throw a??new m("unavailable",0,"no attempt was made")}backoff(e,t,r){if(r?.aborted)return Promise.resolve();let o=t?.retryAfterSeconds,a=o!==void 0?o*1e3:2**(e-1)*250,i=2**(e-1)*250,l=Math.min(Math.max(a,i),Ne);return new Promise(d=>{let c=this.options.clock,g=false,h=false,b,f=()=>{g||(g=true,h&&c.clearTimeout(b),r?.removeEventListener("abort",f),d());};r?.addEventListener("abort",f,{once:true}),b=c.setTimeout(f,l),h=true,g&&c.clearTimeout(b);})}};function He(n){if(n instanceof Uint8Array)return n;if(n==null)return new Uint8Array(0);let e=typeof n=="string"?n:JSON.stringify(n);return new TextEncoder().encode(e)}function L(n){return {...n.keepalive===void 0?{}:{keepalive:n.keepalive},...n.ifMatch===void 0?{}:{headers:{"if-match":n.ifMatch}}}}function Q(n){return n.reason!==void 0?n.reason:new DOMException("The operation was aborted.","AbortError")}function Ve(n){let e=n?.name;return e==="AbortError"||e==="TimeoutError"}function We(n){let e=Ke(n),t=Je(n.body),r={...t===void 0?{}:{details:t},...n.invocationId===void 0?{}:{invocationId:n.invocationId}};return Object.keys(r).length>0&&Z.set(e,r),e}var ze=new Map([["insufficient_funds","insufficient_funds"]]);function Ke(n){let{status:e,body:t}=n,r=Ye(t),o=n.retryAfterSeconds??Qe(t),a=Xe(t),i=a===void 0?void 0:ze.get(a);if(i!==void 0)return new m(i,e,r,void 0,a);switch(e){case 304:return new m("not_modified",e,r);case 400:case 422:return new m("invalid",e,r,void 0,a);case 403:return new m("forbidden",e,r,void 0,a);case 404:return new m("not_found",e,r,void 0,a);case 402:return new m("insufficient_funds",e,r,void 0,a);case 409:return new m("conflict",e,r,void 0,a);case 412:return new m("precondition_failed",e,r,void 0,a);case 413:return new m("too_large",e,r,void 0,a);case 429:return new m("rate_limited",e,r,o,a);default:return new m("unavailable",e,r,o,a)}}function Ye(n){if(typeof n=="string")return n;let e=n?.message;if(typeof e=="string")return e;let t=n?.error;if(typeof t=="string")return t;let r=t?.message;return typeof r=="string"?r:"no detail"}function Je(n){let t=n?.error?.details;return t===null?void 0:t}function Xe(n){let t=n?.error?.code;return typeof t=="string"&&t.length>0?t:void 0}function Qe(n){let e=n?.retryAfterSeconds;return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:void 0}function Ze(n){let e=n.lastIndexOf("@");return (e===-1?n:`***@${n.slice(e+1)}`).slice(0,200)}var en=new Set(F);function nn(n){if(!n)return {};let e={};for(let[t,r]of Object.entries(n))en.has(t.toLowerCase())&&(e[t.toLowerCase()]=r);return e}function ne(n){let e=n.fetch??globalThis.fetch.bind(globalThis);return J(async(t,r)=>{let o;try{o=new URL(n.baseUrl.replace(/\/+$/,""));}catch{throw new y("the configured baseUrl is not a valid URL. Its value is withheld here rather than written to logs. This is a configuration fault, not a server failure.")}let a;try{a=new URL(o.href.replace(/\/+$/,"")+t.path);}catch{throw new y(`${JSON.stringify(typeof t.path=="string"?Ze(t.path):"(a non-string path)")} does not form a valid URL against ${o.protocol}//${o.host}. This is a malformed request, not a server failure.`)}if(a.protocol!==o.protocol||a.host!==o.host)throw new y(`${JSON.stringify(a.pathname)} resolves to ${a.protocol}//${a.host}, not ${o.protocol}//${o.host}. A request path may not change the request's origin \u2014 it carries the play token.`);if(a.username!==""||a.password!==""){let f=o.username!==""||o.password!=="",p=a.username!==o.username||a.password!==o.password,k=f?p?"baseUrl (and possibly the request path)":"baseUrl":"request path";throw new y(`the ${k} puts credentials in the URL, which cannot be fetched (${t.method} ${a.pathname}). The value is withheld here rather than written to logs. This is a malformed request, not a server failure.`)}for(let[f,p]of Object.entries(n.defaultQuery??{}))a.searchParams.has(f)||a.searchParams.set(f,p);let i=t.bytes,l=t.bytes===void 0?void 0:"application/octet-stream";try{t.body!==void 0&&(i=JSON.stringify(t.body),l="application/json");}catch(f){throw new y(`the body of ${t.method} ${JSON.stringify(a.pathname)} could not be serialised to JSON: ${String(f)}. This is a bug in the request, not a server failure \u2014 retrying sends the same unserialisable value.`)}let d=await e(a.toString(),{method:t.method,headers:{authorization:`Bearer ${r}`,...l===void 0?{}:{"content-type":l},...nn(t.headers)},body:i,signal:t.signal,...t.keepalive===void 0?{}:{keepalive:t.keepalive},credentials:"omit",mode:"cors",redirect:"error",cache:"no-store",referrerPolicy:"no-referrer"}),c=null;if(t.responseType==="bytes"&&d.status>=200&&d.status<300)c=new Uint8Array(await d.arrayBuffer());else {let f=await d.text();if(f.length>0)try{c=JSON.parse(f);}catch{c=f;}}let g=d.headers.get("etag"),h=tn(d.headers.get("retry-after")),b=d.headers.get("x-gem-invocation-id");return {status:d.status,body:c,...g===null?{}:{etag:g},...h===void 0?{}:{retryAfterSeconds:h},...b===null||b===""?{}:{invocationId:b}}},n)}function tn(n){if(n===null)return;let e=n.trim();if(!/^\d+$/.test(e))return;let t=Number(e);return Number.isFinite(t)?t:void 0}var rn="gem-web-bridge";var I=65536,te=8,U=5e3,re=1024;var N=Object.create(null);var oe=Object.create(null);function on(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 an(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 sn(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function ln(n){return /^[a-z_]+$/.test(n)}function H(n,e,t){if(e>te)return `nesting deeper than ${te}`;if(++t.nodes>U)return `more than ${U} values`;if(t.bytes>I)return `over the ${I} 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 o=n;if(t.seen.has(o))return "a repeated or cyclic object reference";if(t.seen.add(o),Array.isArray(n)){if(n.length>re)return `an array of ${n.length} entries, over the ${re} limit`;t.bytes+=2;for(let l of n){let d=H(l,e+1,t);if(d)return d}return null}let a=Object.getPrototypeOf(n);if(a!==Object.prototype&&a!==null)return "a value that is not a plain object";t.bytes+=2;let i=n;for(let l in i){if(!Object.prototype.hasOwnProperty.call(i,l))continue;if(++t.nodes>U)return `more than ${U} values`;if(t.bytes+=l.length*3+4,t.bytes>I)return `over the ${I} byte limit`;let d=H(i[l],e+1,t);if(d)return d}return null}function dn(n){let e={nodes:0,bytes:0,seen:new Set},t=H(n,0,e);return t?`message contains ${t}`:e.bytes>I?`message is over the ${I} byte limit`:null}var un={origin:[on,"not a well-formed origin"],url:[an,"not a safe absolute http(s) URL"],bcp47:[sn,"not a well-formed BCP 47 tag"],code:[ln,"not a code of lower-case letters and underscores"]};function ae(n,e,t){for(let r of e){let o=t===""?r.n:`${t}.${r.n}`,a=n[r.n];if(a==null){if(r.o)continue;return `${o}: required`}let i=se(a,r,o);if(i)return i}return null}function se(n,e,t){switch(e.k){case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e.max)return `${t}: exceeds ${e.max} characters`;if(e.f){let[r,o]=un[e.f];if(!r(n))return `${t}: ${o}`}return null}case "i":return typeof n!="number"||!Number.isInteger(n)?`${t}: expected integer`:n<e.lo||n>e.hi?`${t}: out of range ${e.lo}..${e.hi}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let r=e.v;return r.includes(n)?null:`${t}: not one of ${r.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:ae(n,e.fs,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.max)return `${t}: exceeds ${e.max} items`;let r=e.it,o=`${t}[]`;for(let a of n){if(a==null){if(r.o)continue;return `${o}: required`}let i=se(a,r,o);if(i)return i}return null}default:{let r=e.k;return `${t}: unknown kind ${String(r)}`}}}var ie=Object.create(null);function u(n,e,t,r){return N[n]=t,r&&(oe[n]=r),ie[n]=o=>{let a=dn(o);if(a)return a;if(typeof o!="object"||o===null||Array.isArray(o))return "message: expected object";let i=o;return i.gem!==rn?"gem: wrong protocol marker":i.type!==n?`type: expected ${n}`:ae(i,e,"")}}u("gem.handshake",[{n:"versions",k:"o",fs:[{n:"min",k:"i",lo:1,hi:1e3},{n:"max",k:"i",lo:1,hi:1e3}]},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}},{n:"embedder",k:"s",max:2048,f:"origin"},{n:"unbidden",k:"b",o:1}],30);u("gem.handshake.accept",[{n:"v",k:"i",lo:1,hi:1e3},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}}],5);u("gem.token",[{n:"token",k:"s",max:4096},{n:"requestId",k:"s",o:1,max:64},{n:"generation",k:"i",lo:0,hi:2147483647},{n:"apiBaseUrl",k:"s",o:1,max:512,f:"url"},{n:"playerId",k:"s",max:64},{n:"gameId",k:"s",max:64},{n:"channel",k:"e",v:["dev","test","release"]},{n:"gameVersion",k:"s",o:1,max:64},{n:"expiresAt",k:"i",lo:0,hi:4102444800},{n:"chainEndsAt",k:"i",lo:0,hi:4102444800},{n:"allowDevTools",k:"b",o:1}],30);u("gem.token.denied",[{n:"requestId",k:"s",max:64},{n:"retryable",k:"b"},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:36e5},{n:"reason",k:"e",v:["chain_exhausted","unavailable"]},{n:"correlationId",k:"s",max:64}],30);u("gem.player",[{n:"untrusted",k:"o",fs:[{n:"displayName",k:"s",max:64},{n:"displayTag",k:"s",o:1,max:80},{n:"displayHandle",k:"s",max:80}]},{n:"playerId",k:"s",o:1,max:64},{n:"discriminator",k:"s",o:1,max:8},{n:"avatarIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatarIconId",k:"s",o:1,max:64},{n:"avatar3dId",k:"s",o:1,max:64},{n:"avatar3dModelUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatar3dIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatars3d",k:"a",o:1,max:8,it:{n:"",k:"o",fs:[{n:"family",k:"s",max:32},{n:"id",k:"s",max:64},{n:"modelUrl",k:"s",max:256,f:"url"},{n:"iconUrl",k:"s",max:256,f:"url"}]}},{n:"badges",k:"a",o:1,max:2,it:{n:"",k:"o",fs:[{n:"itemId",k:"s",max:64},{n:"iconUrl",k:"s",max:2048,f:"url"}]}},{n:"displayTag",k:"s",max:8},{n:"avatarUrl",k:"s",o:1,max:2048,f:"url"},{n:"boost",k:"o",o:1,fs:[{n:"multipliers",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"currencyId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"multiplierBp",k:"i",lo:1e4,hi:1e6}]}},{n:"expiresInDays",k:"i",o:1,lo:1,hi:7}]}],12);u("gem.pause",[{n:"paused",k:"b"}],120);u("gem.prefs",[{n:"reducedMotion",k:"b"},{n:"muted",k:"b"},{n:"showFrameStats",k:"b",o:1},{n:"locale",k:"s",max:35,f:"bcp47"},{n:"recommended",k:"o",o:1,fs:[{n:"volume",k:"i",lo:0,hi:100},{n:"batterySaver",k:"b"},{n:"keyboardLayout",k:"e",v:["wasd","arrows"]},{n:"gamepadConfirm",k:"e",v:["south","east"]},{n:"vibration",k:"b"},{n:"lookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"gamepadLookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"invertLookY",k:"b",o:1},{n:"touchMovement",k:"s",o:1,max:16,f:"code"},{n:"graphicsQuality",k:"s",o:1,max:16,f:"code"},{n:"graphicsQualityAuto",k:"b",o:1}]}],60);u("gem.cohorts",[{n:"cohorts",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"experimentId",k:"s",max:36},{n:"variantKey",k:"s",max:32}]}}],12,"cohorts");u("gem.earnAllowanceChanged",[],30,"earnAllowance");u("gem.viewport",[{n:"insets",k:"o",fs:[{n:"top",k:"i",lo:0,hi:4096},{n:"right",k:"i",lo:0,hi:4096},{n:"bottom",k:"i",lo:0,hi:4096},{n:"left",k:"i",lo:0,hi:4096}]},{n:"reserved",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384},{n:"control",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384}]}]}}],60,"viewport");u("gem.deviceSettings",[{n:"settings",k:"j",o:1}],12,"deviceSettings");u("gem.willTerminate",[{n:"reason",k:"e",v:["grant_expiring","session_ended","navigating","kill_switch"]},{n:"graceMs",k:"i",lo:0,hi:6e4}],10);u("gem.roomEvent",[{n:"event",k:"e",v:["updated","match_started","match_ready","match_failed","version_stale"]},{n:"roomId",k:"s",max:64},{n:"matchId",k:"s",o:1,max:64},{n:"version",k:"i",o:1,lo:0,hi:2147483647},{n:"reason",k:"s",o:1,max:64},{n:"platform",k:"e",o:1,v:["universal","mobile"]}],120,"rooms");u("gem.socketState",[{n:"connected",k:"b"},{n:"reason",k:"e",o:1,v:["connecting","dropped","watchdog","terminal"]}],12,"socketState");u("gem.seatedEvent",[{n:"roomId",k:"s",max:64}],60,"partySeating");u("gem.autoStartFailedEvent",[{n:"roomId",k:"s",max:64},{n:"code",k:"s",max:64}],30,"autoStartFailure");u("gem.ticketEvent",[{n:"event",k:"e",v:["queued","matched","expired","cancelled","failed"]},{n:"ticketId",k:"s",max:64},{n:"partySize",k:"i",o:1,lo:1,hi:64},{n:"queuedAt",k:"i",o:1,lo:0,hi:4102444800},{n:"expiresAt",k:"i",o:1,lo:0,hi:4102444800},{n:"reason",k:"s",o:1,max:64},{n:"roomId",k:"s",o:1,max:64}],60,"ticketEvents");u("gem.signalEvent",[{n:"event",k:"e",v:["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1800,"p2p");u("gem.dedicatedEvent",[{n:"event",k:"e",v:["answer","error","matchEnded"]},{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",o:1,max:10240},{n:"sessionId",k:"s",o:1,max:64},{n:"code",k:"s",o:1,max:64},{n:"message",k:"s",o:1,max:256},{n:"reason",k:"s",o:1,max:64}],120,"dedicated");u("gem.partyResult",[{n:"requestId",k:"s",max:64},{n:"partyId",k:"s",o:1,max:64}],30,"parties");u("gem.clusterTarget",[{n:"clusterTargetId",k:"s",max:64},{n:"clusterTargetIds",k:"a",o:1,max:3,it:{n:"",k:"s",max:64}}],12,"clusterTarget");u("gem.devtools",[{n:"action",k:"e",v:["open","close","toggle"]}],30,"devtools");u("gem.screenshot",[{n:"maxWidth",k:"i",lo:1,hi:7680},{n:"maxHeight",k:"i",lo:1,hi:4320}],6,"screenshot");u("gem.screenshot.denied",[{n:"reason",k:"e",v:["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]},{n:"retryable",k:"b"}],12,"screenshot");u("gem.requestContext",[],12,"feedbackContext");u("gem.gameContext",[{n:"context",k:"j"}],12,"feedbackContext");u("gem.signal",[{n:"signal",k:"e",v:["join","offer","answer","ice","migrationAck","hostUnreachable"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1200,"p2p");u("gem.dedicatedOffer",[{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",max:10240},{n:"intent",k:"e",v:["initial","ice_restart","full_reconnect"]}],60,"dedicated");u("gem.partyRequest",[{n:"requestId",k:"s",max:64}],12,"parties");u("gem.ready",[],5);u("gem.engaged",[],5,"engagement");u("gem.progress",[{n:"loaded",k:"i",lo:0,hi:1099511627776},{n:"total",k:"i",lo:0,hi:1099511627776}],600);u("gem.requestToken",[{n:"requestId",k:"s",max:64},{n:"reason",k:"e",v:["initial","expiring","rejected"]}],12);u("gem.requestLocalServer",[{n:"requestId",k:"s",max:64},{n:"gameRoomId",k:"s",max:64}],6,"localHost");u("gem.localServer",[{n:"requestId",k:"s",max:64},{n:"serverId",k:"s",o:1,max:128}],30,"localHost");u("gem.localServer.denied",[{n:"requestId",k:"s",max:64},{n:"retryable",k:"b"},{n:"reason",k:"e",v:["refused","unavailable"]}],30,"localHost");u("gem.exit",[{n:"reason",k:"s",o:1,max:64}],5);u("gem.openExternal",[{n:"url",k:"s",max:2048,f:"url"}],6);u("gem.requestFullscreen",[{n:"enabled",k:"b"}],30);u("gem.audioState",[{n:"muted",k:"b"}],120,"audioState");u("gem.rewardCue",[{n:"currencySlug",k:"s",max:32},{n:"expected",k:"i",o:1,lo:1,hi:1e9},{n:"origin",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384}]}],60,"rewardCue");u("gem.walletDisplay",[{n:"visible",k:"b"},{n:"currencies",k:"a",o:1,max:8,it:{n:"",k:"s",max:64}},{n:"score",k:"b",o:1}],60,"walletDisplay");u("gem.controlsDeclare",[{n:"uses",k:"o",fs:[{n:"lookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"gamepadLookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"invertLookY",k:"s",o:1,max:16,f:"code"},{n:"touchMovement",k:"s",o:1,max:16,f:"code"}]}],60,"controls");u("gem.menu",[{n:"settings",k:"b"},{n:"items",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:32},{n:"label",k:"s",max:40}]}}],60,"gameMenu");u("gem.menuSelect",[{n:"entry",k:"e",v:["settings","item"]},{n:"id",k:"s",o:1,max:32}],60,"gameMenu");u("gem.gemCorner",[{n:"corner",k:"e",v:["top-left","top-right","bottom-left","bottom-right"]}],12,"gemCorner");u("gem.requestShare",[{n:"params",k:"a",max:16,it:{n:"",k:"o",fs:[{n:"key",k:"s",max:64},{n:"value",k:"s",max:512}]}}],6,"share");u("gem.requestAvatarPicker",[{n:"tab",k:"e",o:1,v:["2d","3d"]}],6,"avatarPicker");u("gem.vibrate",[{n:"pattern",k:"a",max:99,it:{n:"",k:"i",lo:0,hi:1e4}}],600,"vibration");u("gem.frameStats",[{n:"fps",k:"i",lo:0,hi:1e3},{n:"frameTimeP95Ms",k:"i",lo:0,hi:1e5},{n:"counts",k:"e",o:1,v:["pageFrames","gameDraws"]}],120,"frameStats");u("gem.portraitBorders",[{n:"name",k:"s",o:1,max:40}],60,"portraitBorders");u("gem.error",[{n:"code",k:"e",v:["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]},{n:"message",k:"s",o:1,max:256}],60);u("gem.requestPurchase",[{n:"requestId",k:"s",max:64},{n:"purchaseKind",k:"e",v:["item","recipe"]},{n:"targetId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"quantity",k:"i",o:1,lo:1,hi:10},{n:"catalogId",k:"s",o:1,max:64}],12,"purchases");u("gem.requestAvatar3dPurchase",[{n:"requestId",k:"s",max:64},{n:"itemId",k:"s",max:64},{n:"currencySlug",k:"s",max:32}],12,"avatar3dPurchases");u("gem.purchaseResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["ok","declined","unavailable"]},{n:"reason",k:"s",o:1,max:64,f:"code"},{n:"purchaseId",k:"s",o:1,max:64}],30,"purchases");u("gem.deviceSettings.put",[{n:"requestId",k:"s",max:64},{n:"settings",k:"j"}],30,"deviceSettings");u("gem.deviceSettings.result",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["stored","too_large","unavailable"]}],30,"deviceSettings");u("gem.requestAd",[{n:"requestId",k:"s",max:64},{n:"placement",k:"s",max:32},{n:"format",k:"e",v:["interstitial","rewarded"]}],6,"ads");u("gem.adResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["shown","no_ad","unavailable"]}],12,"ads");u("gem.signalMoment",[{n:"requestId",k:"s",max:64},{n:"kind",k:"e",v:["level_end","run_end","match_end","menu_return","other_break"]},{n:"placement",k:"s",o:1,max:32}],6,"moments");u("gem.momentResult",[{n:"requestId",k:"s",max:64},{n:"shown",k:"b"}],12,"moments");u("gem.requestSignIn",[{n:"requestId",k:"s",max:64}],6,"signIn");u("gem.signInResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["signed_in","declined","unavailable"]}],12,"signIn");u("gem.requestFriendChallenge",[{n:"requestId",k:"s",max:64},{n:"playerId",k:"s",max:64},{n:"template",k:"s",max:64},{n:"params",k:"j",o:1}],12,"friendChallenges");u("gem.friendChallengeResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["accepted","cancelled","refused","unavailable","rate_limited","invalid"]},{n:"code",k:"s",o:1,max:64},{n:"param",k:"s",o:1,max:64},{n:"channel",k:"s",o:1,max:16},{n:"configVersion",k:"i",o:1,lo:0,hi:9007199254740991},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:864e5},{n:"detail",k:"s",o:1,max:300}],30,"friendChallenges");Object.freeze(ie);N.__malformed=60;Object.freeze(N);Object.freeze(oe);var E={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)};function le(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}var cn=1e4;function de(n,e){return V(n,e)}function V(n,e){if(typeof n.serverToken!="function"||typeof n.onServerToken!="function")throw new Error("this runtime delivers no game-server token: host.serverToken/onServerToken are missing. The server image running this bundle predates token delivery \u2014 the bundle cannot call gem-api on it.");let t=i=>({token:i.token,expiresAt:i.expiresAt,chainEndsAt:Number.POSITIVE_INFINITY}),r=null,o=new B({clock:e.clock,...e.onStateChange===void 0?{}:{onStateChange:e.onStateChange},request:()=>new Promise((i,l)=>{let d=e.clock.setTimeout(()=>{r=null,l({reason:"unavailable",retryable:true});},e.timeoutMs??cn);r={resolve:c=>{e.clock.clearTimeout(d),i(c);},reject:l,timer:d};})}),a=n.onServerToken(i=>{let l=t(i),d=r;if(d){r=null,d.resolve(l);return}o.accept(l);});return {source:o,dispose(){a(),o.dispose(),r&&(e.clock.clearTimeout(r.timer),r.reject({reason:"unavailable",retryable:false}),r=null);}}}var z={};Ie(z,{acceptTransfer:()=>mn,addEntityProgression:()=>gn,adminAction:()=>pn,cancelInventoryTransfer:()=>yn,claimEntityProgressionRewards:()=>fn,closeGameRoom:()=>hn,createEntityBatchOrder:()=>_n,createEntityOrder:()=>bn,createSession:()=>vn,deleteEntityGameSetting:()=>Rn,deleteEntitySave:()=>xn,deleteSession:()=>kn,deleteShowcaseValue:()=>Sn,executeEntityRecipe:()=>wn,finalizeSaveUpload:()=>An,getChannelAssignment:()=>Pn,getDropRates:()=>On,getEntityCurrency:()=>Tn,getEntityGameSetting:()=>Gn,getEntityInventory:()=>Cn,getEntityInventoryItem:()=>In,getEntityLeaderboard:()=>En,getEntityOrder:()=>Mn,getEntityProgression:()=>Ln,getEntityRecipeAvailability:()=>qn,getEntityRecipeUsage:()=>$n,getEntitySave:()=>jn,getEvent:()=>Bn,getGameRoomV2:()=>Fn,getLeaderboard:()=>Un,getPlayerGameProfile:()=>Nn,getPurchaseCatalog:()=>Dn,getResolvedTemplate:()=>Hn,getRoomArgs:()=>Vn,getShowcaseValue:()=>Wn,getTransferDetails:()=>zn,ingestAnalyticsEvents:()=>W,initiateInventoryTransfer:()=>Kn,invokeFunction:()=>Yn,kickPlayer:()=>Jn,listChallenges:()=>Xn,listEntityGameSettings:()=>Qn,listEntityOrders:()=>Zn,listEntityProgressions:()=>et,listEntitySaves:()=>nt,listEvents:()=>tt,listGameRooms:()=>rt,listResolvedTemplates:()=>ot,listTransfers:()=>at,maxEntityProgression:()=>st,putEntityGameSetting:()=>it,putRoomArgs:()=>lt,putShowcaseValue:()=>dt,rejectTransfer:()=>ut,reportMatchPlayers:()=>ct,requestSaveUploadUrl:()=>mt,resetEntityProgression:()=>gt,setMatchBackfill:()=>pt,simulateEntityOrder:()=>yt,transferHostV2:()=>ft,updateGameRoomV2:()=>ht});function s(n){let e=String(n);if(!/^[A-Za-z0-9_.-]{1,128}$/.test(e)||e==="."||e==="..")throw new TypeError(`${JSON.stringify(e)} is not a usable path segment \u2014 it is interpolated into a URL`);return encodeURIComponent(e)}async function mn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/accept`;return n.client.post(r,void 0,t?.signal)}async function gn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/add`;return n.client.post(i,o,a?.signal)}async function pn(n,e,t,r){let o=`/v1/voice/sessions/${s(e)}/admin`;return n.client.post(o,t,r?.signal)}async function yn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/cancel`;return n.client.post(r,void 0,t?.signal)}async function fn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/claim`;return n.client.post(i,o,a?.signal)}async function hn(n,e,t){let r=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.delete(r,t?.signal)}async function _n(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/batch`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function bn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function vn(n,e,t){return n.client.post("/v1/voice/sessions",e,t?.signal)}async function Rn(n,e,t,r,o){let a=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.delete(a,o?.signal)}async function xn(n,e,t,r,o){let a=`/v1/saves/${s(e)}/${s(t)}/${s(r)}`;return n.client.delete(a,o?.signal)}async function kn(n,e,t){let r=`/v1/voice/sessions/${s(e)}`;return n.client.delete(r,t?.signal)}async function Sn(n,e,t,r){let o=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.delete(o,r?.signal)}async function wn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/recipes/execute`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function An(n,e,t,r,o,a){let i=`/v1/saves/${s(e)}/${s(t)}/${s(r)}/finalize`;return n.client.post(i,o,a?.signal)}async function Pn(n,e,t){let r=`/v1/games/${s(n.gameId())}/config/channels/${s(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function On(n,e,t){let r=`/v1/inventory/loot-tables/${s(e)}/drop-rates`;return n.client.get(r,t?.signal)}async function Tn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/currencies/${s(r)}`;return n.client.get(a,o?.signal)}async function Gn(n,e,t,r,o){let a=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function Cn(n,e,t,r){let o=new URLSearchParams;r?.include_buckets!==void 0&&o.set("include_buckets",String(r?.include_buckets)),r?.include_custom_data!==void 0&&o.set("include_custom_data",String(r?.include_custom_data)),r?.currency_limit!==void 0&&o.set("currency_limit",String(r?.currency_limit)),r?.currency_cursor!==void 0&&o.set("currency_cursor",String(r?.currency_cursor)),r?.item_limit!==void 0&&o.set("item_limit",String(r?.item_limit)),r?.item_cursor!==void 0&&o.set("item_cursor",String(r?.item_cursor)),r?.progression_limit!==void 0&&o.set("progression_limit",String(r?.progression_limit)),r?.progression_cursor!==void 0&&o.set("progression_cursor",String(r?.progression_cursor));let a=o.toString(),i=`/v1/inventory/${s(e)}/${s(t)}`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function In(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/items/${s(r)}`;return n.client.get(a,o?.signal)}async function En(n,e,t,r,o){let a=new URLSearchParams;o?.bucket_1!==void 0&&a.set("bucket_1",String(o?.bucket_1)),o?.bucket_2!==void 0&&a.set("bucket_2",String(o?.bucket_2)),o?.limit!==void 0&&a.set("limit",String(o?.limit)),o?.periods_back!==void 0&&a.set("periods_back",String(o?.periods_back)),o?.period!==void 0&&a.set("period",String(o?.period)),o?.player_id!==void 0&&a.set("player_id",String(o?.player_id));let i=a.toString(),l=`/v1/leaderboards/${s(e)}/${s(t)}/${s(r)}`+(i?`?${i}`:"");return n.client.get(l,o?.signal)}async function Mn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/${s(r)}`;return n.client.get(a,o?.signal)}async function Ln(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}`;return n.client.get(a,o?.signal)}async function qn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/catalogs/${s(r)}/recipes/${s(o)}/availability`;return n.client.get(i,a?.signal)}async function $n(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit));let a=o.toString(),i=`/v1/inventory/${s(e)}/${s(t)}/recipe-usage`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function jn(n,e,t,r,o){let a=`/v1/saves/${s(e)}/${s(t)}/${s(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function Bn(n,e,t){let r=`/v1/events/${s(e)}`;return n.client.get(r,t?.signal)}async function Fn(n,e,t){let r=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Un(n,e,t){let r=new URLSearchParams;t?.bucket_1!==void 0&&r.set("bucket_1",String(t?.bucket_1)),t?.bucket_2!==void 0&&r.set("bucket_2",String(t?.bucket_2)),t?.periods_back!==void 0&&r.set("periods_back",String(t?.periods_back)),t?.period!==void 0&&r.set("period",String(t?.period)),t?.player_id!==void 0&&r.set("player_id",String(t?.player_id));let o=r.toString(),a=`/v1/leaderboards/${s(e)}`+(o?`?${o}`:"");return n.client.get(a,t?.signal)}async function Nn(n,e,t){let r=`/v1/profiles/players/${s(e)}`;return n.client.get(r,t?.signal)}async function Dn(n,e){let t=new URLSearchParams;e?.config_version!==void 0&&t.set("config_version",String(e?.config_version));let r=t.toString(),o="/v1/inventory/platform-funded-game-inventory-purchase-catalog"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function Hn(n,e,t){let r=`/v1/game-room-templates/${s(e)}`;return n.client.get(r,t?.signal)}async function Vn(n,e,t){let r=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/room-args`;return n.client.get(r,t?.signal)}async function Wn(n,e,t,r){let o=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.get(o,r?.signal)}async function zn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}`;return n.client.get(r,t?.signal)}async function W(n,e,t){let r=`/v1/games/${s(n.gameId())}/analytics/events`;return n.client.post(r,e,t?.signal,{replaySafe:true,retryRateLimited:false})}async function Kn(n,e,t){return n.client.post("/v1/inventory/transfers",e,t?.signal)}async function Yn(n,e,t){return n.client.postBytes("/v1/functions/invoke",e,t?.signal)}async function Jn(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/kick/${s(t)}`;return n.client.post(o,void 0,r?.signal)}async function Xn(n,e){let t=new URLSearchParams;e?.periods_back!==void 0&&t.set("periods_back",String(e?.periods_back)),e?.player_id!==void 0&&t.set("player_id",String(e?.player_id));let r=t.toString(),o="/v1/challenges"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function Qn(n,e,t,r){let o=`/v1/settings/${s(e)}/${s(t)}`;return n.client.get(o,r?.signal)}async function Zn(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit)),r?.cursor!==void 0&&o.set("cursor",String(r?.cursor));let a=o.toString(),i=`/v1/inventory/${s(e)}/${s(t)}/orders`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function et(n,e,t,r){let o=`/v1/inventory/${s(e)}/${s(t)}/progressions`;return n.client.get(o,r?.signal)}async function nt(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit)),r?.cursor!==void 0&&o.set("cursor",String(r?.cursor));let a=o.toString(),i=`/v1/saves/${s(e)}/${s(t)}`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function tt(n,e){let t=new URLSearchParams;e?.include_inactive!==void 0&&t.set("include_inactive",String(e?.include_inactive)),e?.limit!==void 0&&t.set("limit",String(e?.limit)),e?.offset!==void 0&&t.set("offset",String(e?.offset));let r=t.toString(),o="/v1/events"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function rt(n,e){let t=new URLSearchParams;e?.status!==void 0&&t.set("status",String(e?.status)),e?.game_channel!==void 0&&t.set("game_channel",String(e?.game_channel)),e?.game_room_type!==void 0&&t.set("game_room_type",String(e?.game_room_type)),e?.template_id!==void 0&&t.set("template_id",String(e?.template_id)),e?.template_slug!==void 0&&t.set("template_slug",String(e?.template_slug)),e?.limit!==void 0&&t.set("limit",String(e?.limit)),e?.cursor!==void 0&&t.set("cursor",String(e?.cursor));let r=t.toString(),o=`/v1/games/${s(n.gameId())}/game-rooms`+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function ot(n,e){return n.client.get("/v1/game-room-templates",e?.signal)}async function at(n,e){let t=new URLSearchParams;e.entity_type!==void 0&&t.set("entity_type",String(e.entity_type)),e.entity_id!==void 0&&t.set("entity_id",String(e.entity_id)),e?.direction!==void 0&&t.set("direction",String(e?.direction)),e?.status!==void 0&&t.set("status",String(e?.status)),e?.cursor!==void 0&&t.set("cursor",String(e?.cursor)),e?.limit!==void 0&&t.set("limit",String(e?.limit));let r=t.toString(),o="/v1/inventory/transfers"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function st(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/max`;return n.client.post(i,o,a?.signal)}async function it(n,e,t,r,o,a){let i=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.put(i,o,a?.signal,{...a?.ifMatch===void 0?{}:{ifMatch:a.ifMatch}})}async function lt(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/room-args`;return n.client.put(o,t,r?.signal)}async function dt(n,e,t,r,o){let a=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.put(a,r,o?.signal)}async function ut(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/reject`;return n.client.post(r,void 0,t?.signal)}async function ct(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/matches/${s(e)}/players/report`;return n.client.post(o,t,r?.signal)}async function mt(n,e,t,r,o,a){let i=`/v1/saves/${s(e)}/${s(t)}/${s(r)}/upload-url`;return n.client.post(i,o,a?.signal,{replaySafe:true})}async function gt(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/reset`;return n.client.post(i,o,a?.signal)}async function pt(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/matches/${s(e)}/backfill`;return n.client.put(o,t,r?.signal)}async function yt(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/simulate`;return n.client.post(a,r,o?.signal)}async function ft(n,e,t,r){let o=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}/transfer/${s(t)}`;return n.client.post(o,void 0,r?.signal)}async function ht(n,e,t,r){let o=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.patch(o,t,r?.signal,{...r?.ifMatch===void 0?{}:{ifMatch:r.ifMatch}})}var ue="gem_";function ce(n,e){let t=e?.retryAfterSeconds,r=typeof t=="number"&&Number.isFinite(t)&&t>0?t*1e3:0,o=1e4*2**Math.min(Math.max(0,n-1),16);return Math.min(Math.max(r,o),3e5)}var vt=1e4,Rt=50,xt=1e3,kt=65536-4*1024,me=n=>typeof n=="number"?n:0,St=n=>typeof n=="string"?n:"";function ge(){let n=globalThis.crypto;return typeof n?.randomUUID=="function"?n.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.floor(Math.random()*16);return (e==="x"?t:t&3|8).toString(16)})}function wt(n){return n?.retryable===true}function At(n,e){let t=n??{},r=Array.isArray(t.rejected)?t.rejected.map(o=>{let a=o??{};return {index:me(a.index),code:St(a.code)}}):[];return {accepted:me(t.accepted),rejected:r,dropped:e,duplicate:t.duplicate===true}}function pe(n,e){return {event_uid:n.uid,name:n.name,offset_ms:Math.round(n.atMs)-e,player_id:n.playerId,dimensions:n.dimensions,measures:n.measures}}function ye(n){let e=Math.max(1,Math.floor(n.maxQueue??xt)),t={client:n.client,gameId:()=>n.gameId},r=[],o=0,a=null,i=false,l=null,d=0,c=0,g=false,h=false,b=v=>{n.log?.(v);},f=()=>{a!==null&&(n.clock.clearTimeout(a),a=null);},p=()=>Math.max(0,c-n.clock.now()),k=()=>{a!==null||i||l!==null||(a=n.clock.setTimeout(()=>{a=null,P().catch(()=>{});},Math.max(vt,p())));},w=()=>{let v=Math.min(r.length,100),R=128;for(let O=0;O<v;O+=1){let T=r[O];if(T===void 0)break;if(R+=JSON.stringify(pe(T,0)).length+1,R>kt)return Math.max(1,O)}return v};async function P(v){for(;l!==null;)await l.catch(()=>{});if(r.length===0)return {accepted:0,rejected:[],dropped:0,duplicate:false};let R=Te(v);l=R;try{return await R}finally{l===R&&(l=null),r.length>0&&k();}}async function Te(v){f();let R=r.splice(0,w()),O=o;o=0;let T=Math.round(n.clock.now()),j={batch_id:ge(),sent_at_ms:T,events:R.map(S=>pe(S,T))};try{let S=await W(t,j,v===void 0?void 0:{signal:v}),G=At(S,O);d=0,c=0;for(let Ge of G.rejected)b(`[gem] analytics: an event was refused (${Ge.code}). See the analytics_events section of your game config.`);return G}catch(S){if(wt(S)){for(r=[...R,...r],o+=O;r.length>e;)r.shift(),o+=1;d+=1,c=n.clock.now()+ce(d,S);}else b(`[gem] analytics: ${R.length} event(s) were discarded \u2014 the platform refused the batch and will refuse it again: ${String(S)}`);throw S}}return {track(v,R,O={}){if(i)return;if(typeof v!="string"||v===""){h||(h=true,b(`[gem] analytics: '${R}' was not recorded \u2014 an event from a server has to name the player it is about. A game-server token names no player, so the platform cannot infer one; pass the id from join() or host.players().`));return}if(R.startsWith(ue)){g||(g=true,b(`[gem] analytics: '${R}' was not recorded \u2014 the gem_ prefix is reserved, and the platform's own events are client-sourced, so a server may not emit one at all. Name your event something else.`));return}let T={},j={};for(let[S,G]of Object.entries(O))typeof G=="string"?T[S]=G:typeof G=="number"&&Number.isFinite(G)&&(j[S]=G);for(;r.length>=e;)r.shift(),o+=1;r.push({uid:ge(),playerId:v,name:R,atMs:n.clock.now(),dimensions:T,measures:j}),r.length>=Rt&&l===null&&p()===0?P().catch(()=>{}):k();},flush:v=>P(v),get pending(){return r.length},dispose(){i=true,f(),r=[],o=0;}}}var Pt=new Map([["function_error",false],["function_failed",false],["function_timeout",false],["function_not_published",false],["function_input_too_large",false],["functions_not_configured",false],["forbidden",false],["function_capacity_unavailable",true],["function_host_unreachable",true],["function_host_error",true],["rate_limited",true],["service_unavailable",true]]),q=class extends m{constructor(e,t){super(e.reason,e.status,e.detail,e.retryAfterSeconds,e.code),this.name="GemFunctionError",Object.assign(this,t);}get retryable(){return (this.code===void 0?void 0:Pt.get(this.code))??super.retryable}},M=class{#e;constructor(e){this.#e=e;}async invoke(e,t={}){let r=Ot(e),o;try{o=await this.#e(r,t.signal===void 0?{}:{signal:t.signal});}catch(a){throw a instanceof m?Gt(a):a}return Tt(o)}};function Ot(n){if(n instanceof ArrayBuffer)return new Uint8Array(n);if(ArrayBuffer.isView(n))return new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let e;try{e=JSON.stringify(n);}catch(t){throw new y(`the function input could not be serialised to JSON: ${String(t)}. Pass bytes, or a value JSON can carry. This is a bug in the call, not a server failure.`)}if(e===void 0)throw new y(`the function input is ${typeof n}, which JSON cannot carry. Pass bytes \u2014 an empty Uint8Array for no input \u2014 or a JSON value. This is a bug in the call, not a server failure.`);return new TextEncoder().encode(e)}function Tt(n){let{bytes:e}=n,t=()=>new TextDecoder("utf-8",{fatal:true}).decode(e);return {bytes:e,...n.invocationId===void 0?{}:{invocationId:n.invocationId},text:t,json:()=>JSON.parse(t())}}function Gt(n){let{details:e,invocationId:t}=ee(n),o=e?.untrusted?.message,a=e?.reason;return new q(n,{...t===void 0?{}:{invocationId:t},...n.code==="function_error"&&typeof o=="string"?{functionMessage:o}:{},...n.code==="function_failed"&&typeof a=="string"?{failure:a}:{}})}var A=n=>typeof n=="string"?n:"",Ct=/(?:Z|[+-]\d{2}:?\d{2})$/i;function fe(n){let e=A(n);if(!Ct.test(e))return 0;let t=Date.parse(e);return Number.isFinite(t)?Math.floor(t/1e3):0}var It=/^[0-9a-f]{1,64}$/,Et=/^[0-9a-f]{16}$/,Mt=/^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/,Lt=/^(?:[01]\d|2[0-3]):[0-5]\d$/;function qt(n){let e=A(n),t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!t)return;let[r,o,a]=[Number(t[1]),Number(t[2]),Number(t[3])],i=new Date(Date.UTC(r,o-1,a));return i.getUTCFullYear()===r&&i.getUTCMonth()===o-1&&i.getUTCDate()===a?e:void 0}var $t=4096,jt=new TextEncoder;function Bt(n){let e=A(n.challenge_data),t=A(n.challenge_data_hash);return e===""||!Et.test(t)?{}:jt.encode(e).length>$t?{}:{data:e,dataHash:t}}function Ft(n){let e=n??{},t=fe(e.period_start_at),r=fe(e.period_ends_at);if(t===0||r===0)return;let o=A(e.slug),a=A(e.seed),i=qt(e.period_local_date),l=A(e.boundary),d=A(e.reset_time);return {id:A(e.progression_definition_id),...o===""?{}:{slug:o},window:A(e.window),periodStartAt:t,periodEndsAt:r,...i===void 0?{}:{periodLocalDate:i},boundary:Mt.test(l)?l:"",resetTime:Lt.test(d)?d:"",seed:It.test(a)?a:"",...Bt(e)}}function he(n){if(n!==void 0&&(!Number.isInteger(n)||n<0||n>400))throw new TypeError(`periodsBack must be an integer between 0 and 400, got ${String(n)}`)}function _e(n){let e=n??{};return Array.isArray(e.challenges)?e.challenges.map(Ft).filter(t=>t!==void 0):[]}function be(n){return {async list(e){let{playerId:t,periodsBack:r,signal:o}=e??{};if(typeof t!="string"||t==="")throw new TypeError("gem.challenges.list needs { playerId }: a game-server token names no player, so the platform cannot tell whose period you mean. Pass the id from join() or gem.players().");he(r);let a=await n({player_id:t,...r===void 0?{}:{periods_back:r},...o===void 0?{}:{signal:o}});return _e(a)}}}function Ut(n){let e={};for(let[t,r]of Object.entries(z))if(typeof r=="function"){let o=r;e[t]=(...a)=>o(n,...a);}return e}function D(n,e={}){let t=e.clock??E,r=e.baseUrl??n.apiBaseUrl;if(!r)throw new Error("this runtime names no gem-api host (host.apiBaseUrl is missing). The server image running this bundle predates token delivery \u2014 the bundle cannot call gem-api on it.");let o=V(n,{clock:t,...e.onAuthStateChange===void 0?{}:{onStateChange:e.onAuthStateChange}}),a=ne({baseUrl:r,tokenSource:o.source,defaultQuery:{game_id:n.gameId,channel:n.channel},...e.fetch===void 0?{}:{fetch:e.fetch}}),i=new C({transport:a,clock:t}),l={gameId:n.gameId,channel:n.channel,matchId:n.matchId,...n.gameRoomId===void 0?{}:{gameRoomId:n.gameRoomId}},d=Ut({client:i,gameId:()=>l.gameId}),c=l.gameRoomId,g=n.endMatch,h=n.log,b=ye({client:i,clock:t,gameId:l.gameId,...typeof h=="function"?{log:p=>h.call(n,p)}:{}});return {identity:l,client:i,api:d,match:{end:async()=>{if(g===void 0)throw new Error("this runtime cannot end the match: the server image running this bundle predates host.endMatch, and only the platform half holds the allocation proof an end needs.");await g.call(n);},setBackfill:async(p,k)=>{let w=await d.setMatchBackfill(l.matchId,{open:p.open,...p.seats===void 0?{}:{seats:p.seats}},k===void 0?{}:{signal:k});return {open:w.open,seats:w.seats??null}},reportPlayers:async(p,k)=>(await d.reportMatchPlayers(l.matchId,{players:p.map(P=>({player_id:P.playerId,state:P.state}))},k===void 0?{}:{signal:k})).players.map(P=>({playerId:P.player_id,outcome:P.outcome}))},analytics:b,functions:new M(d.invokeFunction),challenges:be(d.listChallenges),...c===void 0?{}:{room:{get:p=>d.getGameRoomV2(c,p),args:async p=>{let k=await d.getGameRoomV2(c,p?.signal===void 0?{}:{signal:p.signal}),w=k.modified?k.body:void 0;if(w?.room_args==null||w.room_args_template==null)throw new m("not_found",404,"this room's template declares no room args",void 0,"room_args_not_defined");return {room_args:w.room_args,room_args_template:w.room_args_template}}}},dispose:()=>{b.dispose(),o.dispose();}}}var Nt=["explicit","stale_cleanup","game_room_closed","replaced","migration_failed","host_left","insufficient_players","all_left","expired","migration_rate_exceeded","game_room_gone","failed","idle","reclaimed"],Dt=new Set(Nt);function ve(n){return typeof n=="string"&&Dt.has(n)?n:"unknown"}var ke="[gem: use gem.text.toPlain()]",Re=new Set,xe;function Ht(n){try{if(Re.has(n))return;let e=globalThis.location;if(xe??=!!e?.port&&le(e.hostname??""),!xe)return;Re.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${ke}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
|
|
1
|
+
var Ce=Object.defineProperty;var Ie=(n,e)=>{for(var t in e)Ce(n,t,{get:e[t],enumerable:true});};globalThis.__gemWebSdkVersion="[gem-web-sdk:sdkVersion:13.4.3]";var Ee={initialDelayMs:2e3,maxDelayMs:6e4};function Me(n,e){return e!==void 0?!e:n==="chain_exhausted"}var B=class{constructor(e){this.options=e;}options;grant=null;state="pending";failure=null;generation=0;inFlight=null;rejected=false;retry=null;failures=0;notBefore=0;disposed=false;get currentState(){return this.state}get lastFailure(){return this.failure}async get(){if(this.state==="lost")throw new _(this.failure??"unavailable","the session cannot be renewed");let e=this.seconds();if(this.grant&&e<this.grant.expiresAt-this.renewBefore())return this.grant.token;if(this.pastChainCeiling())throw new _("chain_exhausted","the token chain reached its ceiling");if(this.inFlight===null&&this.options.clock.now()<this.notBefore)throw new _(this.failure??"unavailable","the embedder asked for no new token yet; one is asked for when that wait is over");return this.refresh(this.nextReason())}pastChainCeiling(){return this.grant===null||this.seconds()<this.grant.chainEndsAt?false:(this.enter("lost","chain_exhausted"),true)}nextReason(){return this.rejected?"rejected":this.grant?"expiring":"initial"}invalidate(e){e!==void 0&&this.grant!==null&&this.grant.token!==e||this.rejected&&this.grant===null||(this.grant=null,this.generation+=1,this.inFlight=null,this.rejected=true,this.state!=="lost"&&this.enter("pending"));}accept(e){this.state!=="lost"&&(this.grant=e,this.rejected=false,this.enter("ready"));}close(e="unavailable"){this.grant=null,this.generation+=1,this.inFlight=null,this.enter("lost",e);}dispose(){this.disposed=true,this.cancelRetry();}scheduleRetry(e){if(this.cancelRetry(),this.failures+=1,this.notBefore=e===void 0?0:this.options.clock.now()+e,this.disposed)return;let{initialDelayMs:t,maxDelayMs:r}=Ee,o=Math.min(r,t*2**(this.failures-1)),a=Math.max(o/2,e??0)+Math.random()*(o/2);this.retry={handle:this.options.clock.setTimeout(()=>this.retryNow(),a)};}retryNow(){this.retry=null,this.notBefore=0,!(this.state==="lost"||this.pastChainCeiling())&&this.refresh(this.nextReason()).catch(()=>{});}cancelRetry(){this.retry!==null&&(this.options.clock.clearTimeout(this.retry.handle),this.retry=null);}refresh(e){if(this.inFlight)return this.inFlight;this.cancelRetry();let t=this.generation;this.enter("pending");let r=this.options.request(e).then(o=>{if(t!==this.generation)throw new _("internal","token arrived for a superseded generation");return this.grant=o,this.rejected=false,this.enter("ready"),o.token}).catch(o=>{let a=Le(o),{retryable:i,retryAfterMs:l}=o??{};throw t===this.generation&&(Me(a,typeof i=="boolean"?i:void 0)?this.enter("lost",a):(this.scheduleRetry(typeof l=="number"&&Number.isFinite(l)&&l>0?l:void 0),this.enter("degraded",a))),o instanceof _?o:new _(a,String(o))}).finally(()=>{this.inFlight===r&&(this.inFlight=null);});return this.inFlight=r,r}enter(e,t){(e==="ready"||e==="lost")&&this.cancelRetry(),e==="ready"&&(this.failures=0,this.notBefore=0),this.failure=t??(e==="ready"?null:this.failure),this.state!==e&&(this.state=e,this.options.onStateChange?.(e,t));}renewBefore(){return this.options.renewBeforeSeconds??60}seconds(){return Math.floor(this.options.clock.now()/1e3)}},_=class n extends Error{constructor(t,r){super(`auth ${t}: ${r}`);this.reason=t;this.name="AuthError";}reason;static isAuthError(t){if(t instanceof n)return true;let r=t;return r?.name==="AuthError"&&typeof r.reason=="string"}};function Le(n){if(n instanceof _)return n.reason;let e=n?.reason;return typeof e=="string"&&["chain_exhausted","unavailable","timeout","internal"].includes(e)?e:"internal"}var y=class n extends Error{retryable=false;constructor(e){super(e),this.name="TransportRefusal";}static isRefusal(e){if(e instanceof n)return true;let t=e;return t?.name==="TransportRefusal"&&t.retryable===false}},F=["if-match","if-none-match"],Y=4096;function qe(n){if(!n)return;let e={};for(let[t,r]of Object.entries(n)){let o=t.toLowerCase();if(!F.includes(o)||r===void 0)continue;let a=typeof r=="string"?$e(r):r;if(typeof a!="string"||a.length>Y||!je(a))throw new y(`the ${o} header value cannot be sent \u2014 it is not a string, is longer than ${Y} characters, or contains a character outside the range an HTTP header value may carry. This is a malformed request, not a server failure; the value is withheld here rather than written to logs.`);a!==""&&(e[o]=a);}return Object.freeze(e)}function $e(n){let e=o=>o===32||o===9||o===10||o===13,t=0,r=n.length;for(;t<r&&e(n.charCodeAt(t));)t++;for(;r>t&&e(n.charCodeAt(r-1));)r--;return n.slice(t,r)}function je(n){for(let e=0;e<n.length;e++){let t=n.charCodeAt(e);if(t!==9&&(t<32||t===127||t>255))return false}return true}function Be(n){let{bytes:e}=n;if(e!==void 0){if(Object.prototype.toString.call(e)!=="[object Uint8Array]")throw new y("the raw body is not a Uint8Array, so it cannot be sent as bytes. This is a malformed request, not a server failure.");if(n.body!==void 0)throw new y("the request carries both a JSON body and a raw body, and only one can be sent. This is a malformed request, not a server failure.");return new Uint8Array(e)}}function J(n,e){let t=e.replayUnauthorizedOnce??true;return {async request(r){let o={...r},a=qe(o.headers),i=Be(o),l=Object.freeze({...o,headers:a,...i===void 0?{}:{bytes:i}}),d=await e.tokenSource.get(),c=await n(l,d);if(c.status!==401||!t)return c;e.tokenSource.invalidate(d);let g=await e.tokenSource.get(),h=await n(l,g);if(h.status===401)throw e.tokenSource.close("unavailable"),new _("unavailable","the server refused a freshly minted token");return h}}}function X(n){let e=typeof n=="string"?new TextEncoder().encode(n):n;return btoa(Fe(e))}function Fe(n){let t="";for(let r=0;r<n.length;r+=32768)t+=String.fromCharCode(...n.subarray(r,r+32768));return t}var m=class extends Error{constructor(t,r,o,a,i){super(`${t} (${r}): ${o}`);this.reason=t;this.status=r;this.detail=o;this.retryAfterSeconds=a;this.code=i;this.name="GemApiError";}reason;status;detail;retryAfterSeconds;code;get retryable(){return this.reason==="unavailable"||this.reason==="rate_limited"}},Z=new WeakMap;function ee(n){return Z.get(n)??{}}var Ue=new Set([408,429,500,502,503,504]),Ne=6e4;function De(n){if(n===void 0)return 2;if(!Number.isInteger(n)||n<0||n>10)throw new y(`maxRetries must be a whole number between 0 and 10; received ${JSON.stringify(n)}. This is a configuration fault, not a server failure.`);return n}var C=class{constructor(e){this.options=e;}options;async get(e,t){return this.send({method:"GET",path:e,signal:t},true)}async getWithEtag(e,t={}){let r=await this.sendRaw({method:"GET",path:e,...t.signal===void 0?{}:{signal:t.signal},...t.ifNoneMatch===void 0?{}:{headers:{"if-none-match":t.ifNoneMatch}}},true),o=r.etag===void 0?{}:{etag:r.etag};return r.status===304?{modified:false,...o}:{modified:true,body:r.body,...o}}async put(e,t,r,o={}){return this.send({method:"PUT",path:e,body:t,signal:r,...L(o)},o.replaySafe===true,o.retryRateLimited!==false)}async post(e,t,r,o={}){return this.send({method:"POST",path:e,body:t,signal:r,...L(o)},o.replaySafe===true,o.retryRateLimited!==false)}async postBytes(e,t,r,o={}){let a=await this.sendRaw({method:"POST",path:e,bytes:t,responseType:"bytes",signal:r,...L(o)},o.replaySafe===true,o.retryRateLimited!==false);return {bytes:He(a.body),...a.invocationId===void 0?{}:{invocationId:a.invocationId}}}async patch(e,t,r,o={}){return this.send({method:"PATCH",path:e,body:t,signal:r,...L(o)},o.replaySafe===true,o.retryRateLimited!==false)}async delete(e,t,r={}){return this.send({method:"DELETE",path:e,signal:t,...L(r)},r.replaySafe===true,r.retryRateLimited!==false)}async send(e,t,r=true){return (await this.sendRaw(e,t,r)).body}async sendRaw(e,t,r=true){let o=t?De(this.options.maxRetries)+1:1,a=null;if(e.signal?.aborted)throw Q(e.signal);for(let i=0;i<o;i++){if(i>0&&await this.backoff(i,a,e.signal),e.signal?.aborted)throw Q(e.signal);let l;try{l=await this.options.transport.request(e);}catch(c){if(_.isAuthError(c)||y.isRefusal(c)||e.signal?.aborted||Ve(c))throw c;a=new m("unavailable",0,String(c));continue}if(l.status>=200&&l.status<300||l.status===304)return l;let d=We(l);if(!Ue.has(l.status)||l.status===429&&!r)throw d;a=d;}throw a??new m("unavailable",0,"no attempt was made")}backoff(e,t,r){if(r?.aborted)return Promise.resolve();let o=t?.retryAfterSeconds,a=o!==void 0?o*1e3:2**(e-1)*250,i=2**(e-1)*250,l=Math.min(Math.max(a,i),Ne);return new Promise(d=>{let c=this.options.clock,g=false,h=false,b,f=()=>{g||(g=true,h&&c.clearTimeout(b),r?.removeEventListener("abort",f),d());};r?.addEventListener("abort",f,{once:true}),b=c.setTimeout(f,l),h=true,g&&c.clearTimeout(b);})}};function He(n){if(n instanceof Uint8Array)return n;if(n==null)return new Uint8Array(0);let e=typeof n=="string"?n:JSON.stringify(n);return new TextEncoder().encode(e)}function L(n){return {...n.keepalive===void 0?{}:{keepalive:n.keepalive},...n.ifMatch===void 0?{}:{headers:{"if-match":n.ifMatch}}}}function Q(n){return n.reason!==void 0?n.reason:new DOMException("The operation was aborted.","AbortError")}function Ve(n){let e=n?.name;return e==="AbortError"||e==="TimeoutError"}function We(n){let e=Ke(n),t=Je(n.body),r={...t===void 0?{}:{details:t},...n.invocationId===void 0?{}:{invocationId:n.invocationId}};return Object.keys(r).length>0&&Z.set(e,r),e}var ze=new Map([["insufficient_funds","insufficient_funds"]]);function Ke(n){let{status:e,body:t}=n,r=Ye(t),o=n.retryAfterSeconds??Qe(t),a=Xe(t),i=a===void 0?void 0:ze.get(a);if(i!==void 0)return new m(i,e,r,void 0,a);switch(e){case 304:return new m("not_modified",e,r);case 400:case 422:return new m("invalid",e,r,void 0,a);case 403:return new m("forbidden",e,r,void 0,a);case 404:return new m("not_found",e,r,void 0,a);case 402:return new m("insufficient_funds",e,r,void 0,a);case 409:return new m("conflict",e,r,void 0,a);case 412:return new m("precondition_failed",e,r,void 0,a);case 413:return new m("too_large",e,r,void 0,a);case 429:return new m("rate_limited",e,r,o,a);default:return new m("unavailable",e,r,o,a)}}function Ye(n){if(typeof n=="string")return n;let e=n?.message;if(typeof e=="string")return e;let t=n?.error;if(typeof t=="string")return t;let r=t?.message;return typeof r=="string"?r:"no detail"}function Je(n){let t=n?.error?.details;return t===null?void 0:t}function Xe(n){let t=n?.error?.code;return typeof t=="string"&&t.length>0?t:void 0}function Qe(n){let e=n?.retryAfterSeconds;return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:void 0}function Ze(n){let e=n.lastIndexOf("@");return (e===-1?n:`***@${n.slice(e+1)}`).slice(0,200)}var en=new Set(F);function nn(n){if(!n)return {};let e={};for(let[t,r]of Object.entries(n))en.has(t.toLowerCase())&&(e[t.toLowerCase()]=r);return e}function ne(n){let e=n.fetch??globalThis.fetch.bind(globalThis);return J(async(t,r)=>{let o;try{o=new URL(n.baseUrl.replace(/\/+$/,""));}catch{throw new y("the configured baseUrl is not a valid URL. Its value is withheld here rather than written to logs. This is a configuration fault, not a server failure.")}let a;try{a=new URL(o.href.replace(/\/+$/,"")+t.path);}catch{throw new y(`${JSON.stringify(typeof t.path=="string"?Ze(t.path):"(a non-string path)")} does not form a valid URL against ${o.protocol}//${o.host}. This is a malformed request, not a server failure.`)}if(a.protocol!==o.protocol||a.host!==o.host)throw new y(`${JSON.stringify(a.pathname)} resolves to ${a.protocol}//${a.host}, not ${o.protocol}//${o.host}. A request path may not change the request's origin \u2014 it carries the play token.`);if(a.username!==""||a.password!==""){let f=o.username!==""||o.password!=="",p=a.username!==o.username||a.password!==o.password,k=f?p?"baseUrl (and possibly the request path)":"baseUrl":"request path";throw new y(`the ${k} puts credentials in the URL, which cannot be fetched (${t.method} ${a.pathname}). The value is withheld here rather than written to logs. This is a malformed request, not a server failure.`)}for(let[f,p]of Object.entries(n.defaultQuery??{}))a.searchParams.has(f)||a.searchParams.set(f,p);let i=t.bytes,l=t.bytes===void 0?void 0:"application/octet-stream";try{t.body!==void 0&&(i=JSON.stringify(t.body),l="application/json");}catch(f){throw new y(`the body of ${t.method} ${JSON.stringify(a.pathname)} could not be serialised to JSON: ${String(f)}. This is a bug in the request, not a server failure \u2014 retrying sends the same unserialisable value.`)}let d=await e(a.toString(),{method:t.method,headers:{authorization:`Bearer ${r}`,...l===void 0?{}:{"content-type":l},...nn(t.headers)},body:i,signal:t.signal,...t.keepalive===void 0?{}:{keepalive:t.keepalive},credentials:"omit",mode:"cors",redirect:"error",cache:"no-store",referrerPolicy:"no-referrer"}),c=null;if(t.responseType==="bytes"&&d.status>=200&&d.status<300)c=new Uint8Array(await d.arrayBuffer());else {let f=await d.text();if(f.length>0)try{c=JSON.parse(f);}catch{c=f;}}let g=d.headers.get("etag"),h=tn(d.headers.get("retry-after")),b=d.headers.get("x-gem-invocation-id");return {status:d.status,body:c,...g===null?{}:{etag:g},...h===void 0?{}:{retryAfterSeconds:h},...b===null||b===""?{}:{invocationId:b}}},n)}function tn(n){if(n===null)return;let e=n.trim();if(!/^\d+$/.test(e))return;let t=Number(e);return Number.isFinite(t)?t:void 0}var rn="gem-web-bridge";var I=65536,te=8,U=5e3,re=1024;var N=Object.create(null);var oe=Object.create(null);function on(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 an(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 sn(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function ln(n){return /^[a-z_]+$/.test(n)}function H(n,e,t){if(e>te)return `nesting deeper than ${te}`;if(++t.nodes>U)return `more than ${U} values`;if(t.bytes>I)return `over the ${I} 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 o=n;if(t.seen.has(o))return "a repeated or cyclic object reference";if(t.seen.add(o),Array.isArray(n)){if(n.length>re)return `an array of ${n.length} entries, over the ${re} limit`;t.bytes+=2;for(let l of n){let d=H(l,e+1,t);if(d)return d}return null}let a=Object.getPrototypeOf(n);if(a!==Object.prototype&&a!==null)return "a value that is not a plain object";t.bytes+=2;let i=n;for(let l in i){if(!Object.prototype.hasOwnProperty.call(i,l))continue;if(++t.nodes>U)return `more than ${U} values`;if(t.bytes+=l.length*3+4,t.bytes>I)return `over the ${I} byte limit`;let d=H(i[l],e+1,t);if(d)return d}return null}function dn(n){let e={nodes:0,bytes:0,seen:new Set},t=H(n,0,e);return t?`message contains ${t}`:e.bytes>I?`message is over the ${I} byte limit`:null}var un={origin:[on,"not a well-formed origin"],url:[an,"not a safe absolute http(s) URL"],bcp47:[sn,"not a well-formed BCP 47 tag"],code:[ln,"not a code of lower-case letters and underscores"]};function ae(n,e,t){for(let r of e){let o=t===""?r.n:`${t}.${r.n}`,a=n[r.n];if(a==null){if(r.o)continue;return `${o}: required`}let i=se(a,r,o);if(i)return i}return null}function se(n,e,t){switch(e.k){case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e.max)return `${t}: exceeds ${e.max} characters`;if(e.f){let[r,o]=un[e.f];if(!r(n))return `${t}: ${o}`}return null}case "i":return typeof n!="number"||!Number.isInteger(n)?`${t}: expected integer`:n<e.lo||n>e.hi?`${t}: out of range ${e.lo}..${e.hi}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let r=e.v;return r.includes(n)?null:`${t}: not one of ${r.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:ae(n,e.fs,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.max)return `${t}: exceeds ${e.max} items`;let r=e.it,o=`${t}[]`;for(let a of n){if(a==null){if(r.o)continue;return `${o}: required`}let i=se(a,r,o);if(i)return i}return null}default:{let r=e.k;return `${t}: unknown kind ${String(r)}`}}}var ie=Object.create(null);function u(n,e,t,r){return N[n]=t,r&&(oe[n]=r),ie[n]=o=>{let a=dn(o);if(a)return a;if(typeof o!="object"||o===null||Array.isArray(o))return "message: expected object";let i=o;return i.gem!==rn?"gem: wrong protocol marker":i.type!==n?`type: expected ${n}`:ae(i,e,"")}}u("gem.handshake",[{n:"versions",k:"o",fs:[{n:"min",k:"i",lo:1,hi:1e3},{n:"max",k:"i",lo:1,hi:1e3}]},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}},{n:"embedder",k:"s",max:2048,f:"origin"},{n:"unbidden",k:"b",o:1}],30);u("gem.handshake.accept",[{n:"v",k:"i",lo:1,hi:1e3},{n:"capabilities",k:"a",o:1,max:64,it:{n:"",k:"s",max:64}}],5);u("gem.token",[{n:"token",k:"s",max:4096},{n:"requestId",k:"s",o:1,max:64},{n:"generation",k:"i",lo:0,hi:2147483647},{n:"apiBaseUrl",k:"s",o:1,max:512,f:"url"},{n:"playerId",k:"s",max:64},{n:"gameId",k:"s",max:64},{n:"channel",k:"e",v:["dev","test","release"]},{n:"gameVersion",k:"s",o:1,max:64},{n:"expiresAt",k:"i",lo:0,hi:4102444800},{n:"chainEndsAt",k:"i",lo:0,hi:4102444800},{n:"allowDevTools",k:"b",o:1}],30);u("gem.token.denied",[{n:"requestId",k:"s",max:64},{n:"retryable",k:"b"},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:36e5},{n:"reason",k:"e",v:["chain_exhausted","unavailable"]},{n:"correlationId",k:"s",max:64}],30);u("gem.player",[{n:"untrusted",k:"o",fs:[{n:"displayName",k:"s",max:64},{n:"displayTag",k:"s",o:1,max:80},{n:"displayHandle",k:"s",max:80}]},{n:"playerId",k:"s",o:1,max:64},{n:"discriminator",k:"s",o:1,max:8},{n:"avatarIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatarIconId",k:"s",o:1,max:64},{n:"avatar3dId",k:"s",o:1,max:64},{n:"avatar3dModelUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatar3dIconUrl",k:"s",o:1,max:2048,f:"url"},{n:"avatars3d",k:"a",o:1,max:8,it:{n:"",k:"o",fs:[{n:"family",k:"s",max:32},{n:"id",k:"s",max:64},{n:"modelUrl",k:"s",max:256,f:"url"},{n:"iconUrl",k:"s",max:256,f:"url"}]}},{n:"badges",k:"a",o:1,max:2,it:{n:"",k:"o",fs:[{n:"itemId",k:"s",max:64},{n:"iconUrl",k:"s",max:2048,f:"url"}]}},{n:"displayTag",k:"s",max:8},{n:"avatarUrl",k:"s",o:1,max:2048,f:"url"},{n:"boost",k:"o",o:1,fs:[{n:"multipliers",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"currencyId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"multiplierBp",k:"i",lo:1e4,hi:1e6}]}},{n:"expiresInDays",k:"i",o:1,lo:1,hi:7}]}],12);u("gem.pause",[{n:"paused",k:"b"}],120);u("gem.prefs",[{n:"reducedMotion",k:"b"},{n:"muted",k:"b"},{n:"showFrameStats",k:"b",o:1},{n:"locale",k:"s",max:35,f:"bcp47"},{n:"recommended",k:"o",o:1,fs:[{n:"volume",k:"i",lo:0,hi:100},{n:"batterySaver",k:"b"},{n:"keyboardLayout",k:"e",v:["wasd","arrows"]},{n:"gamepadConfirm",k:"e",v:["south","east"]},{n:"vibration",k:"b"},{n:"lookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"gamepadLookSensitivity",k:"i",o:1,lo:20,hi:400},{n:"invertLookY",k:"b",o:1},{n:"touchMovement",k:"s",o:1,max:16,f:"code"},{n:"graphicsQuality",k:"s",o:1,max:16,f:"code"},{n:"graphicsQualityAuto",k:"b",o:1}]}],60);u("gem.cohorts",[{n:"cohorts",k:"a",max:8,it:{n:"",k:"o",fs:[{n:"experimentId",k:"s",max:36},{n:"variantKey",k:"s",max:32}]}}],12,"cohorts");u("gem.earnAllowanceChanged",[],30,"earnAllowance");u("gem.viewport",[{n:"insets",k:"o",fs:[{n:"top",k:"i",lo:0,hi:4096},{n:"right",k:"i",lo:0,hi:4096},{n:"bottom",k:"i",lo:0,hi:4096},{n:"left",k:"i",lo:0,hi:4096}]},{n:"reserved",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384},{n:"control",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384},{n:"width",k:"i",lo:0,hi:16384},{n:"height",k:"i",lo:0,hi:16384}]}]}}],60,"viewport");u("gem.deviceSettings",[{n:"settings",k:"j",o:1}],12,"deviceSettings");u("gem.willTerminate",[{n:"reason",k:"e",v:["grant_expiring","session_ended","navigating","kill_switch"]},{n:"graceMs",k:"i",lo:0,hi:6e4}],10);u("gem.roomEvent",[{n:"event",k:"e",v:["updated","match_started","match_ready","match_failed","version_stale"]},{n:"roomId",k:"s",max:64},{n:"matchId",k:"s",o:1,max:64},{n:"version",k:"i",o:1,lo:0,hi:2147483647},{n:"reason",k:"s",o:1,max:64},{n:"platform",k:"e",o:1,v:["universal","mobile"]}],120,"rooms");u("gem.socketState",[{n:"connected",k:"b"},{n:"reason",k:"e",o:1,v:["connecting","dropped","watchdog","terminal"]}],12,"socketState");u("gem.seatedEvent",[{n:"roomId",k:"s",max:64}],60,"partySeating");u("gem.autoStartFailedEvent",[{n:"roomId",k:"s",max:64},{n:"code",k:"s",max:64}],30,"autoStartFailure");u("gem.ticketEvent",[{n:"event",k:"e",v:["queued","matched","expired","cancelled","failed"]},{n:"ticketId",k:"s",max:64},{n:"partySize",k:"i",o:1,lo:1,hi:64},{n:"queuedAt",k:"i",o:1,lo:0,hi:4102444800},{n:"expiresAt",k:"i",o:1,lo:0,hi:4102444800},{n:"reason",k:"s",o:1,max:64},{n:"roomId",k:"s",o:1,max:64}],60,"ticketEvents");u("gem.signalEvent",[{n:"event",k:"e",v:["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1800,"p2p");u("gem.dedicatedEvent",[{n:"event",k:"e",v:["answer","error","matchEnded"]},{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",o:1,max:10240},{n:"sessionId",k:"s",o:1,max:64},{n:"code",k:"s",o:1,max:64},{n:"message",k:"s",o:1,max:256},{n:"reason",k:"s",o:1,max:64}],120,"dedicated");u("gem.partyResult",[{n:"requestId",k:"s",max:64},{n:"partyId",k:"s",o:1,max:64}],30,"parties");u("gem.clusterTarget",[{n:"clusterTargetId",k:"s",max:64},{n:"clusterTargetIds",k:"a",o:1,max:3,it:{n:"",k:"s",max:64}}],12,"clusterTarget");u("gem.devtools",[{n:"action",k:"e",v:["open","close","toggle"]}],30,"devtools");u("gem.screenshot",[{n:"maxWidth",k:"i",lo:1,hi:7680},{n:"maxHeight",k:"i",lo:1,hi:4320}],6,"screenshot");u("gem.screenshot.denied",[{n:"reason",k:"e",v:["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]},{n:"retryable",k:"b"}],12,"screenshot");u("gem.requestContext",[],12,"feedbackContext");u("gem.gameContext",[{n:"context",k:"j"}],12,"feedbackContext");u("gem.signal",[{n:"signal",k:"e",v:["join","offer","answer","ice","migrationAck","hostUnreachable"]},{n:"matchId",k:"s",max:64},{n:"data",k:"j"}],1200,"p2p");u("gem.dedicatedOffer",[{n:"matchId",k:"s",max:64},{n:"sdp",k:"s",max:10240},{n:"intent",k:"e",v:["initial","ice_restart","full_reconnect"]}],60,"dedicated");u("gem.partyRequest",[{n:"requestId",k:"s",max:64}],12,"parties");u("gem.ready",[],5);u("gem.engaged",[],5,"engagement");u("gem.progress",[{n:"loaded",k:"i",lo:0,hi:1099511627776},{n:"total",k:"i",lo:0,hi:1099511627776}],600);u("gem.requestToken",[{n:"requestId",k:"s",max:64},{n:"reason",k:"e",v:["initial","expiring","rejected"]}],12);u("gem.requestLocalServer",[{n:"requestId",k:"s",max:64},{n:"gameRoomId",k:"s",max:64}],6,"localHost");u("gem.localServer",[{n:"requestId",k:"s",max:64},{n:"serverId",k:"s",o:1,max:128}],30,"localHost");u("gem.localServer.denied",[{n:"requestId",k:"s",max:64},{n:"retryable",k:"b"},{n:"reason",k:"e",v:["refused","unavailable"]}],30,"localHost");u("gem.exit",[{n:"reason",k:"s",o:1,max:64}],5);u("gem.openExternal",[{n:"url",k:"s",max:2048,f:"url"}],6);u("gem.requestFullscreen",[{n:"enabled",k:"b"}],30);u("gem.audioState",[{n:"muted",k:"b"}],120,"audioState");u("gem.rewardCue",[{n:"currencySlug",k:"s",max:32},{n:"expected",k:"i",o:1,lo:1,hi:1e9},{n:"origin",k:"o",o:1,fs:[{n:"x",k:"i",lo:0,hi:16384},{n:"y",k:"i",lo:0,hi:16384}]}],60,"rewardCue");u("gem.walletDisplay",[{n:"visible",k:"b"},{n:"currencies",k:"a",o:1,max:8,it:{n:"",k:"s",max:64}},{n:"score",k:"b",o:1}],60,"walletDisplay");u("gem.controlsDeclare",[{n:"uses",k:"o",fs:[{n:"lookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"gamepadLookSensitivity",k:"s",o:1,max:16,f:"code"},{n:"invertLookY",k:"s",o:1,max:16,f:"code"},{n:"touchMovement",k:"s",o:1,max:16,f:"code"}]}],60,"controls");u("gem.menu",[{n:"settings",k:"b"},{n:"items",k:"a",o:1,max:4,it:{n:"",k:"o",fs:[{n:"id",k:"s",max:32},{n:"label",k:"s",max:40}]}}],60,"gameMenu");u("gem.menuSelect",[{n:"entry",k:"e",v:["settings","item"]},{n:"id",k:"s",o:1,max:32}],60,"gameMenu");u("gem.gemCorner",[{n:"corner",k:"e",v:["top-left","top-right","bottom-left","bottom-right"]}],12,"gemCorner");u("gem.requestShare",[{n:"params",k:"a",max:16,it:{n:"",k:"o",fs:[{n:"key",k:"s",max:64},{n:"value",k:"s",max:512}]}}],6,"share");u("gem.requestAvatarPicker",[{n:"tab",k:"e",o:1,v:["2d","3d"]}],6,"avatarPicker");u("gem.vibrate",[{n:"pattern",k:"a",max:99,it:{n:"",k:"i",lo:0,hi:1e4}}],600,"vibration");u("gem.frameStats",[{n:"fps",k:"i",lo:0,hi:1e3},{n:"frameTimeP95Ms",k:"i",lo:0,hi:1e5},{n:"counts",k:"e",o:1,v:["pageFrames","gameDraws"]}],120,"frameStats");u("gem.portraitBorders",[{n:"name",k:"s",o:1,max:40}],60,"portraitBorders");u("gem.error",[{n:"code",k:"e",v:["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]},{n:"message",k:"s",o:1,max:256}],60);u("gem.requestPurchase",[{n:"requestId",k:"s",max:64},{n:"purchaseKind",k:"e",v:["item","recipe"]},{n:"targetId",k:"s",max:64},{n:"currencySlug",k:"s",max:32},{n:"quantity",k:"i",o:1,lo:1,hi:10},{n:"catalogId",k:"s",o:1,max:64}],12,"purchases");u("gem.requestAvatar3dPurchase",[{n:"requestId",k:"s",max:64},{n:"itemId",k:"s",max:64},{n:"currencySlug",k:"s",max:32}],12,"avatar3dPurchases");u("gem.purchaseResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["ok","declined","unavailable"]},{n:"reason",k:"s",o:1,max:64,f:"code"},{n:"purchaseId",k:"s",o:1,max:64}],30,"purchases");u("gem.deviceSettings.put",[{n:"requestId",k:"s",max:64},{n:"settings",k:"j"}],30,"deviceSettings");u("gem.deviceSettings.result",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["stored","too_large","unavailable"]}],30,"deviceSettings");u("gem.requestAd",[{n:"requestId",k:"s",max:64},{n:"placement",k:"s",max:32},{n:"format",k:"e",v:["interstitial","rewarded"]}],6,"ads");u("gem.adResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["shown","no_ad","unavailable"]}],12,"ads");u("gem.signalMoment",[{n:"requestId",k:"s",max:64},{n:"kind",k:"e",v:["level_end","run_end","match_end","menu_return","other_break"]},{n:"placement",k:"s",o:1,max:32}],6,"moments");u("gem.momentResult",[{n:"requestId",k:"s",max:64},{n:"shown",k:"b"}],12,"moments");u("gem.requestSignIn",[{n:"requestId",k:"s",max:64}],6,"signIn");u("gem.signInResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["signed_in","declined","unavailable"]}],12,"signIn");u("gem.requestFriendChallenge",[{n:"requestId",k:"s",max:64},{n:"playerId",k:"s",max:64},{n:"template",k:"s",max:64},{n:"params",k:"j",o:1}],12,"friendChallenges");u("gem.friendChallengeResult",[{n:"requestId",k:"s",max:64},{n:"outcome",k:"e",v:["accepted","cancelled","refused","unavailable","rate_limited","invalid"]},{n:"code",k:"s",o:1,max:64},{n:"param",k:"s",o:1,max:64},{n:"channel",k:"s",o:1,max:16},{n:"configVersion",k:"i",o:1,lo:0,hi:9007199254740991},{n:"retryAfterMs",k:"i",o:1,lo:0,hi:864e5},{n:"detail",k:"s",o:1,max:300}],30,"friendChallenges");Object.freeze(ie);N.__malformed=60;Object.freeze(N);Object.freeze(oe);var E={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)};function le(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}var cn=1e4;function de(n,e){return V(n,e)}function V(n,e){if(typeof n.serverToken!="function"||typeof n.onServerToken!="function")throw new Error("this runtime delivers no game-server token: host.serverToken/onServerToken are missing. The server image running this bundle predates token delivery \u2014 the bundle cannot call gem-api on it.");let t=i=>({token:i.token,expiresAt:i.expiresAt,chainEndsAt:Number.POSITIVE_INFINITY}),r=null,o=new B({clock:e.clock,...e.onStateChange===void 0?{}:{onStateChange:e.onStateChange},request:()=>new Promise((i,l)=>{let d=e.clock.setTimeout(()=>{r=null,l({reason:"unavailable",retryable:true});},e.timeoutMs??cn);r={resolve:c=>{e.clock.clearTimeout(d),i(c);},reject:l,timer:d};})}),a=n.onServerToken(i=>{let l=t(i),d=r;if(d){r=null,d.resolve(l);return}o.accept(l);});return {source:o,dispose(){a(),o.dispose(),r&&(e.clock.clearTimeout(r.timer),r.reject({reason:"unavailable",retryable:false}),r=null);}}}var z={};Ie(z,{acceptTransfer:()=>mn,addEntityProgression:()=>gn,adminAction:()=>pn,cancelInventoryTransfer:()=>yn,claimEntityProgressionRewards:()=>fn,closeGameRoom:()=>hn,createEntityBatchOrder:()=>_n,createEntityOrder:()=>bn,createSession:()=>vn,deleteEntityGameSetting:()=>Rn,deleteEntitySave:()=>xn,deleteSession:()=>kn,deleteShowcaseValue:()=>Sn,executeEntityRecipe:()=>wn,finalizeSaveUpload:()=>An,getChannelAssignment:()=>Pn,getDropRates:()=>On,getEntityCurrency:()=>Tn,getEntityGameSetting:()=>Gn,getEntityInventory:()=>Cn,getEntityInventoryItem:()=>In,getEntityLeaderboard:()=>En,getEntityOrder:()=>Mn,getEntityProgression:()=>Ln,getEntityRecipeAvailability:()=>qn,getEntityRecipeUsage:()=>$n,getEntitySave:()=>jn,getEvent:()=>Bn,getGameRoomV2:()=>Fn,getLeaderboard:()=>Un,getPlayerGameProfile:()=>Nn,getPurchaseCatalog:()=>Dn,getResolvedTemplate:()=>Hn,getRoomArgs:()=>Vn,getShowcaseValue:()=>Wn,getTransferDetails:()=>zn,ingestAnalyticsEvents:()=>W,initiateInventoryTransfer:()=>Kn,invokeFunction:()=>Yn,kickPlayer:()=>Jn,listChallenges:()=>Xn,listEntityGameSettings:()=>Qn,listEntityOrders:()=>Zn,listEntityProgressions:()=>et,listEntitySaves:()=>nt,listEvents:()=>tt,listGameRooms:()=>rt,listResolvedTemplates:()=>ot,listTransfers:()=>at,maxEntityProgression:()=>st,putEntityGameSetting:()=>it,putRoomArgs:()=>lt,putShowcaseValue:()=>dt,rejectTransfer:()=>ut,reportMatchPlayers:()=>ct,requestSaveUploadUrl:()=>mt,resetEntityProgression:()=>gt,setMatchBackfill:()=>pt,simulateEntityOrder:()=>yt,transferHostV2:()=>ft,updateGameRoomV2:()=>ht});function s(n){let e=String(n);if(!/^[A-Za-z0-9_.-]{1,128}$/.test(e)||e==="."||e==="..")throw new TypeError(`${JSON.stringify(e)} is not a usable path segment \u2014 it is interpolated into a URL`);return encodeURIComponent(e)}async function mn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/accept`;return n.client.post(r,void 0,t?.signal)}async function gn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/add`;return n.client.post(i,o,a?.signal)}async function pn(n,e,t,r){let o=`/v1/voice/sessions/${s(e)}/admin`;return n.client.post(o,t,r?.signal)}async function yn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/cancel`;return n.client.post(r,void 0,t?.signal)}async function fn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/claim`;return n.client.post(i,o,a?.signal)}async function hn(n,e,t){let r=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.delete(r,t?.signal)}async function _n(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/batch`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function bn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function vn(n,e,t){return n.client.post("/v1/voice/sessions",e,t?.signal)}async function Rn(n,e,t,r,o){let a=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.delete(a,o?.signal)}async function xn(n,e,t,r,o){let a=`/v1/saves/${s(e)}/${s(t)}/${s(r)}`;return n.client.delete(a,o?.signal)}async function kn(n,e,t){let r=`/v1/voice/sessions/${s(e)}`;return n.client.delete(r,t?.signal)}async function Sn(n,e,t,r){let o=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.delete(o,r?.signal)}async function wn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/recipes/execute`;return n.client.post(a,r,o?.signal,{...o?.ifMatch===void 0?{}:{ifMatch:o.ifMatch}})}async function An(n,e,t,r,o,a){let i=`/v1/saves/${s(e)}/${s(t)}/${s(r)}/finalize`;return n.client.post(i,o,a?.signal)}async function Pn(n,e,t){let r=`/v1/games/${s(n.gameId())}/config/channels/${s(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function On(n,e,t){let r=`/v1/inventory/loot-tables/${s(e)}/drop-rates`;return n.client.get(r,t?.signal)}async function Tn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/currencies/${s(r)}`;return n.client.get(a,o?.signal)}async function Gn(n,e,t,r,o){let a=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function Cn(n,e,t,r){let o=new URLSearchParams;r?.include_buckets!==void 0&&o.set("include_buckets",String(r?.include_buckets)),r?.include_custom_data!==void 0&&o.set("include_custom_data",String(r?.include_custom_data)),r?.currency_limit!==void 0&&o.set("currency_limit",String(r?.currency_limit)),r?.currency_cursor!==void 0&&o.set("currency_cursor",String(r?.currency_cursor)),r?.item_limit!==void 0&&o.set("item_limit",String(r?.item_limit)),r?.item_cursor!==void 0&&o.set("item_cursor",String(r?.item_cursor)),r?.progression_limit!==void 0&&o.set("progression_limit",String(r?.progression_limit)),r?.progression_cursor!==void 0&&o.set("progression_cursor",String(r?.progression_cursor));let a=o.toString(),i=`/v1/inventory/${s(e)}/${s(t)}`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function In(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/items/${s(r)}`;return n.client.get(a,o?.signal)}async function En(n,e,t,r,o){let a=new URLSearchParams;o?.bucket_1!==void 0&&a.set("bucket_1",String(o?.bucket_1)),o?.bucket_2!==void 0&&a.set("bucket_2",String(o?.bucket_2)),o?.limit!==void 0&&a.set("limit",String(o?.limit)),o?.periods_back!==void 0&&a.set("periods_back",String(o?.periods_back)),o?.period!==void 0&&a.set("period",String(o?.period)),o?.player_id!==void 0&&a.set("player_id",String(o?.player_id));let i=a.toString(),l=`/v1/leaderboards/${s(e)}/${s(t)}/${s(r)}`+(i?`?${i}`:"");return n.client.get(l,o?.signal)}async function Mn(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/${s(r)}`;return n.client.get(a,o?.signal)}async function Ln(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}`;return n.client.get(a,o?.signal)}async function qn(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/catalogs/${s(r)}/recipes/${s(o)}/availability`;return n.client.get(i,a?.signal)}async function $n(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit));let a=o.toString(),i=`/v1/inventory/${s(e)}/${s(t)}/recipe-usage`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function jn(n,e,t,r,o){let a=`/v1/saves/${s(e)}/${s(t)}/${s(r)}`;return n.client.getWithEtag(a,{...o?.signal===void 0?{}:{signal:o.signal},...o?.ifNoneMatch===void 0?{}:{ifNoneMatch:o.ifNoneMatch}})}async function Bn(n,e,t){let r=`/v1/events/${s(e)}`;return n.client.get(r,t?.signal)}async function Fn(n,e,t){let r=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.getWithEtag(r,{...t?.signal===void 0?{}:{signal:t.signal},...t?.ifNoneMatch===void 0?{}:{ifNoneMatch:t.ifNoneMatch}})}async function Un(n,e,t){let r=new URLSearchParams;t?.bucket_1!==void 0&&r.set("bucket_1",String(t?.bucket_1)),t?.bucket_2!==void 0&&r.set("bucket_2",String(t?.bucket_2)),t?.periods_back!==void 0&&r.set("periods_back",String(t?.periods_back)),t?.period!==void 0&&r.set("period",String(t?.period)),t?.player_id!==void 0&&r.set("player_id",String(t?.player_id));let o=r.toString(),a=`/v1/leaderboards/${s(e)}`+(o?`?${o}`:"");return n.client.get(a,t?.signal)}async function Nn(n,e,t){let r=`/v1/profiles/players/${s(e)}`;return n.client.get(r,t?.signal)}async function Dn(n,e){let t=new URLSearchParams;e?.config_version!==void 0&&t.set("config_version",String(e?.config_version));let r=t.toString(),o="/v1/inventory/platform-funded-game-inventory-purchase-catalog"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function Hn(n,e,t){let r=`/v1/game-room-templates/${s(e)}`;return n.client.get(r,t?.signal)}async function Vn(n,e,t){let r=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/room-args`;return n.client.get(r,t?.signal)}async function Wn(n,e,t,r){let o=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.get(o,r?.signal)}async function zn(n,e,t){let r=`/v1/inventory/transfers/${s(e)}`;return n.client.get(r,t?.signal)}async function W(n,e,t){let r=`/v1/games/${s(n.gameId())}/analytics/events`;return n.client.post(r,e,t?.signal,{replaySafe:true,retryRateLimited:false})}async function Kn(n,e,t){return n.client.post("/v1/inventory/transfers",e,t?.signal)}async function Yn(n,e,t){return n.client.postBytes("/v1/functions/invoke",e,t?.signal)}async function Jn(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/kick/${s(t)}`;return n.client.post(o,void 0,r?.signal)}async function Xn(n,e){let t=new URLSearchParams;e?.periods_back!==void 0&&t.set("periods_back",String(e?.periods_back)),e?.player_id!==void 0&&t.set("player_id",String(e?.player_id));let r=t.toString(),o="/v1/challenges"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function Qn(n,e,t,r){let o=`/v1/settings/${s(e)}/${s(t)}`;return n.client.get(o,r?.signal)}async function Zn(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit)),r?.cursor!==void 0&&o.set("cursor",String(r?.cursor));let a=o.toString(),i=`/v1/inventory/${s(e)}/${s(t)}/orders`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function et(n,e,t,r){let o=`/v1/inventory/${s(e)}/${s(t)}/progressions`;return n.client.get(o,r?.signal)}async function nt(n,e,t,r){let o=new URLSearchParams;r?.limit!==void 0&&o.set("limit",String(r?.limit)),r?.cursor!==void 0&&o.set("cursor",String(r?.cursor));let a=o.toString(),i=`/v1/saves/${s(e)}/${s(t)}`+(a?`?${a}`:"");return n.client.get(i,r?.signal)}async function tt(n,e){let t=new URLSearchParams;e?.include_inactive!==void 0&&t.set("include_inactive",String(e?.include_inactive)),e?.limit!==void 0&&t.set("limit",String(e?.limit)),e?.offset!==void 0&&t.set("offset",String(e?.offset));let r=t.toString(),o="/v1/events"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function rt(n,e){let t=new URLSearchParams;e?.status!==void 0&&t.set("status",String(e?.status)),e?.game_channel!==void 0&&t.set("game_channel",String(e?.game_channel)),e?.game_room_type!==void 0&&t.set("game_room_type",String(e?.game_room_type)),e?.template_id!==void 0&&t.set("template_id",String(e?.template_id)),e?.template_slug!==void 0&&t.set("template_slug",String(e?.template_slug)),e?.limit!==void 0&&t.set("limit",String(e?.limit)),e?.cursor!==void 0&&t.set("cursor",String(e?.cursor));let r=t.toString(),o=`/v1/games/${s(n.gameId())}/game-rooms`+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function ot(n,e){return n.client.get("/v1/game-room-templates",e?.signal)}async function at(n,e){let t=new URLSearchParams;e.entity_type!==void 0&&t.set("entity_type",String(e.entity_type)),e.entity_id!==void 0&&t.set("entity_id",String(e.entity_id)),e?.direction!==void 0&&t.set("direction",String(e?.direction)),e?.status!==void 0&&t.set("status",String(e?.status)),e?.cursor!==void 0&&t.set("cursor",String(e?.cursor)),e?.limit!==void 0&&t.set("limit",String(e?.limit));let r=t.toString(),o="/v1/inventory/transfers"+(r?`?${r}`:"");return n.client.get(o,e?.signal)}async function st(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/max`;return n.client.post(i,o,a?.signal)}async function it(n,e,t,r,o,a){let i=`/v1/settings/${s(e)}/${s(t)}/${s(r)}`;return n.client.put(i,o,a?.signal,{...a?.ifMatch===void 0?{}:{ifMatch:a.ifMatch}})}async function lt(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/game-rooms/${s(e)}/room-args`;return n.client.put(o,t,r?.signal)}async function dt(n,e,t,r,o){let a=`/v1/showcase/players/${s(e)}/${s(t)}`;return n.client.put(a,r,o?.signal)}async function ut(n,e,t){let r=`/v1/inventory/transfers/${s(e)}/reject`;return n.client.post(r,void 0,t?.signal)}async function ct(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/matches/${s(e)}/players/report`;return n.client.post(o,t,r?.signal)}async function mt(n,e,t,r,o,a){let i=`/v1/saves/${s(e)}/${s(t)}/${s(r)}/upload-url`;return n.client.post(i,o,a?.signal,{replaySafe:true})}async function gt(n,e,t,r,o,a){let i=`/v1/inventory/${s(e)}/${s(t)}/progressions/${s(r)}/reset`;return n.client.post(i,o,a?.signal)}async function pt(n,e,t,r){let o=`/v1/games/${s(n.gameId())}/matches/${s(e)}/backfill`;return n.client.put(o,t,r?.signal)}async function yt(n,e,t,r,o){let a=`/v1/inventory/${s(e)}/${s(t)}/orders/simulate`;return n.client.post(a,r,o?.signal)}async function ft(n,e,t,r){let o=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}/transfer/${s(t)}`;return n.client.post(o,void 0,r?.signal)}async function ht(n,e,t,r){let o=`/v2/games/${s(n.gameId())}/game-rooms/${s(e)}`;return n.client.patch(o,t,r?.signal,{...r?.ifMatch===void 0?{}:{ifMatch:r.ifMatch}})}var ue="gem_";function ce(n,e){let t=e?.retryAfterSeconds,r=typeof t=="number"&&Number.isFinite(t)&&t>0?t*1e3:0,o=1e4*2**Math.min(Math.max(0,n-1),16);return Math.min(Math.max(r,o),3e5)}var vt=1e4,Rt=50,xt=1e3,kt=65536-4*1024,me=n=>typeof n=="number"?n:0,St=n=>typeof n=="string"?n:"";function ge(){let n=globalThis.crypto;return typeof n?.randomUUID=="function"?n.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.floor(Math.random()*16);return (e==="x"?t:t&3|8).toString(16)})}function wt(n){return n?.retryable===true}function At(n,e){let t=n??{},r=Array.isArray(t.rejected)?t.rejected.map(o=>{let a=o??{};return {index:me(a.index),code:St(a.code)}}):[];return {accepted:me(t.accepted),rejected:r,dropped:e,duplicate:t.duplicate===true}}function pe(n,e){return {event_uid:n.uid,name:n.name,offset_ms:Math.round(n.atMs)-e,player_id:n.playerId,dimensions:n.dimensions,measures:n.measures}}function ye(n){let e=Math.max(1,Math.floor(n.maxQueue??xt)),t={client:n.client,gameId:()=>n.gameId},r=[],o=0,a=null,i=false,l=null,d=0,c=0,g=false,h=false,b=v=>{n.log?.(v);},f=()=>{a!==null&&(n.clock.clearTimeout(a),a=null);},p=()=>Math.max(0,c-n.clock.now()),k=()=>{a!==null||i||l!==null||(a=n.clock.setTimeout(()=>{a=null,P().catch(()=>{});},Math.max(vt,p())));},w=()=>{let v=Math.min(r.length,100),R=128;for(let O=0;O<v;O+=1){let T=r[O];if(T===void 0)break;if(R+=JSON.stringify(pe(T,0)).length+1,R>kt)return Math.max(1,O)}return v};async function P(v){for(;l!==null;)await l.catch(()=>{});if(r.length===0)return {accepted:0,rejected:[],dropped:0,duplicate:false};let R=Te(v);l=R;try{return await R}finally{l===R&&(l=null),r.length>0&&k();}}async function Te(v){f();let R=r.splice(0,w()),O=o;o=0;let T=Math.round(n.clock.now()),j={batch_id:ge(),sent_at_ms:T,events:R.map(S=>pe(S,T))};try{let S=await W(t,j,v===void 0?void 0:{signal:v}),G=At(S,O);d=0,c=0;for(let Ge of G.rejected)b(`[gem] analytics: an event was refused (${Ge.code}). See the analytics_events section of your game config.`);return G}catch(S){if(wt(S)){for(r=[...R,...r],o+=O;r.length>e;)r.shift(),o+=1;d+=1,c=n.clock.now()+ce(d,S);}else b(`[gem] analytics: ${R.length} event(s) were discarded \u2014 the platform refused the batch and will refuse it again: ${String(S)}`);throw S}}return {track(v,R,O={}){if(i)return;if(typeof v!="string"||v===""){h||(h=true,b(`[gem] analytics: '${R}' was not recorded \u2014 an event from a server has to name the player it is about. A game-server token names no player, so the platform cannot infer one; pass the id from join() or host.players().`));return}if(R.startsWith(ue)){g||(g=true,b(`[gem] analytics: '${R}' was not recorded \u2014 the gem_ prefix is reserved, and the platform's own events are client-sourced, so a server may not emit one at all. Name your event something else.`));return}let T={},j={};for(let[S,G]of Object.entries(O))typeof G=="string"?T[S]=G:typeof G=="number"&&Number.isFinite(G)&&(j[S]=G);for(;r.length>=e;)r.shift(),o+=1;r.push({uid:ge(),playerId:v,name:R,atMs:n.clock.now(),dimensions:T,measures:j}),r.length>=Rt&&l===null&&p()===0?P().catch(()=>{}):k();},flush:v=>P(v),get pending(){return r.length},dispose(){i=true,f(),r=[],o=0;}}}var Pt=new Map([["function_error",false],["function_failed",false],["function_timeout",false],["function_not_published",false],["function_input_too_large",false],["functions_not_configured",false],["forbidden",false],["function_capacity_unavailable",true],["function_host_unreachable",true],["function_host_error",true],["rate_limited",true],["service_unavailable",true]]),q=class extends m{constructor(e,t){super(e.reason,e.status,e.detail,e.retryAfterSeconds,e.code),this.name="GemFunctionError",Object.assign(this,t);}get retryable(){return (this.code===void 0?void 0:Pt.get(this.code))??super.retryable}},M=class{#e;constructor(e){this.#e=e;}async invoke(e,t={}){let r=Ot(e),o;try{o=await this.#e(r,t.signal===void 0?{}:{signal:t.signal});}catch(a){throw a instanceof m?Gt(a):a}return Tt(o)}};function Ot(n){if(n instanceof ArrayBuffer)return new Uint8Array(n);if(ArrayBuffer.isView(n))return new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let e;try{e=JSON.stringify(n);}catch(t){throw new y(`the function input could not be serialised to JSON: ${String(t)}. Pass bytes, or a value JSON can carry. This is a bug in the call, not a server failure.`)}if(e===void 0)throw new y(`the function input is ${typeof n}, which JSON cannot carry. Pass bytes \u2014 an empty Uint8Array for no input \u2014 or a JSON value. This is a bug in the call, not a server failure.`);return new TextEncoder().encode(e)}function Tt(n){let{bytes:e}=n,t=()=>new TextDecoder("utf-8",{fatal:true}).decode(e);return {bytes:e,...n.invocationId===void 0?{}:{invocationId:n.invocationId},text:t,json:()=>JSON.parse(t())}}function Gt(n){let{details:e,invocationId:t}=ee(n),o=e?.untrusted?.message,a=e?.reason;return new q(n,{...t===void 0?{}:{invocationId:t},...n.code==="function_error"&&typeof o=="string"?{functionMessage:o}:{},...n.code==="function_failed"&&typeof a=="string"?{failure:a}:{}})}var A=n=>typeof n=="string"?n:"",Ct=/(?:Z|[+-]\d{2}:?\d{2})$/i;function fe(n){let e=A(n);if(!Ct.test(e))return 0;let t=Date.parse(e);return Number.isFinite(t)?Math.floor(t/1e3):0}var It=/^[0-9a-f]{1,64}$/,Et=/^[0-9a-f]{16}$/,Mt=/^[A-Za-z][A-Za-z0-9_+\-/]{0,63}$/,Lt=/^(?:[01]\d|2[0-3]):[0-5]\d$/;function qt(n){let e=A(n),t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!t)return;let[r,o,a]=[Number(t[1]),Number(t[2]),Number(t[3])],i=new Date(Date.UTC(r,o-1,a));return i.getUTCFullYear()===r&&i.getUTCMonth()===o-1&&i.getUTCDate()===a?e:void 0}var $t=4096,jt=new TextEncoder;function Bt(n){let e=A(n.challenge_data),t=A(n.challenge_data_hash);return e===""||!Et.test(t)?{}:jt.encode(e).length>$t?{}:{data:e,dataHash:t}}function Ft(n){let e=n??{},t=fe(e.period_start_at),r=fe(e.period_ends_at);if(t===0||r===0)return;let o=A(e.slug),a=A(e.seed),i=qt(e.period_local_date),l=A(e.boundary),d=A(e.reset_time);return {id:A(e.progression_definition_id),...o===""?{}:{slug:o},window:A(e.window),periodStartAt:t,periodEndsAt:r,...i===void 0?{}:{periodLocalDate:i},boundary:Mt.test(l)?l:"",resetTime:Lt.test(d)?d:"",seed:It.test(a)?a:"",...Bt(e)}}function he(n){if(n!==void 0&&(!Number.isInteger(n)||n<0||n>400))throw new TypeError(`periodsBack must be an integer between 0 and 400, got ${String(n)}`)}function _e(n){let e=n??{};return Array.isArray(e.challenges)?e.challenges.map(Ft).filter(t=>t!==void 0):[]}function be(n){return {async list(e){let{playerId:t,periodsBack:r,signal:o}=e??{};if(typeof t!="string"||t==="")throw new TypeError("gem.challenges.list needs { playerId }: a game-server token names no player, so the platform cannot tell whose period you mean. Pass the id from join() or gem.players().");he(r);let a=await n({player_id:t,...r===void 0?{}:{periods_back:r},...o===void 0?{}:{signal:o}});return _e(a)}}}function Ut(n){let e={};for(let[t,r]of Object.entries(z))if(typeof r=="function"){let o=r;e[t]=(...a)=>o(n,...a);}return e}function D(n,e={}){let t=e.clock??E,r=e.baseUrl??n.apiBaseUrl;if(!r)throw new Error("this runtime names no gem-api host (host.apiBaseUrl is missing). The server image running this bundle predates token delivery \u2014 the bundle cannot call gem-api on it.");let o=V(n,{clock:t,...e.onAuthStateChange===void 0?{}:{onStateChange:e.onAuthStateChange}}),a=ne({baseUrl:r,tokenSource:o.source,defaultQuery:{game_id:n.gameId,channel:n.channel},...e.fetch===void 0?{}:{fetch:e.fetch}}),i=new C({transport:a,clock:t}),l={gameId:n.gameId,channel:n.channel,matchId:n.matchId,...n.gameRoomId===void 0?{}:{gameRoomId:n.gameRoomId}},d=Ut({client:i,gameId:()=>l.gameId}),c=l.gameRoomId,g=n.endMatch,h=n.log,b=ye({client:i,clock:t,gameId:l.gameId,...typeof h=="function"?{log:p=>h.call(n,p)}:{}});return {identity:l,client:i,api:d,match:{end:async()=>{if(g===void 0)throw new Error("this runtime cannot end the match: the server image running this bundle predates host.endMatch, and only the platform half holds the allocation proof an end needs.");await g.call(n);},setBackfill:async(p,k)=>{let w=await d.setMatchBackfill(l.matchId,{open:p.open,...p.seats===void 0?{}:{seats:p.seats}},k===void 0?{}:{signal:k});return {open:w.open,seats:w.seats??null}},reportPlayers:async(p,k)=>(await d.reportMatchPlayers(l.matchId,{players:p.map(P=>({player_id:P.playerId,state:P.state}))},k===void 0?{}:{signal:k})).players.map(P=>({playerId:P.player_id,outcome:P.outcome}))},analytics:b,functions:new M(d.invokeFunction),challenges:be(d.listChallenges),...c===void 0?{}:{room:{get:p=>d.getGameRoomV2(c,p),args:async p=>{let k=await d.getGameRoomV2(c,p?.signal===void 0?{}:{signal:p.signal}),w=k.modified?k.body:void 0;if(w?.room_args==null||w.room_args_template==null)throw new m("not_found",404,"this room's template declares no room args",void 0,"room_args_not_defined");return {room_args:w.room_args,room_args_template:w.room_args_template}}}},dispose:()=>{b.dispose(),o.dispose();}}}var Nt=["explicit","stale_cleanup","game_room_closed","replaced","migration_failed","host_left","insufficient_players","all_left","expired","migration_rate_exceeded","game_room_gone","failed","idle","reclaimed"],Dt=new Set(Nt);function ve(n){return typeof n=="string"&&Dt.has(n)?n:"unknown"}var ke="[gem: use gem.text.toPlain()]",Re=new Set,xe;function Ht(n){try{if(Re.has(n))return;let e=globalThis.location;if(xe??=!!e?.port&&le(e.hostname??""),!xe)return;Re.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${ke}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
|
|
2
2
|
${new Error().stack}`);}catch{}}var x=class{#e;#n;constructor(e,t="player text"){this.#e=e,this.#n=t,Object.freeze(this);}toString(){return Ht(this.#n),ke}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function $(n,e){return new x(n,e)}var Se={toPlain(n){return x.unwrap(n)},compare(n,e,t){let r=n instanceof x?x.unwrap(n):n,o=e instanceof x?x.unwrap(e):e;if(typeof Intl>"u"||typeof Intl.Collator!="function")return r<o?-1:r>o?1:0;let a;try{a=new Intl.Collator(t);}catch{a=new Intl.Collator;}return a.compare(r,o)},equals(n,e){let t=n instanceof x?x.unwrap(n):n,r=e instanceof x?x.unwrap(e):e;return t.normalize("NFC")===r.normalize("NFC")}};var we="the match has not started yet: the runtime hands over the host at `init`, and this was read before that. Read it from `init` or later \u2014 a bundle that needs a value at module scope is reading it one call too early.";function K(n,e={}){let t=e.clock??E,r=()=>{let l=n();if(l===null)throw new Error(we);return l},o=null,a=()=>{if(o===null)throw new Error(we);return o};return {server:{get identity(){return a().identity},get client(){return a().client},get api(){return a().api},get match(){return a().match},get analytics(){return a().analytics},get functions(){return a().functions},get challenges(){return a().challenges},get room(){return a().room},dispose(){o?.dispose();},get sizeClass(){return r().sizeClass},get roomArgs(){return r().roomArgs},get roster(){return r().roster},players(){return r().players()},profile(l){let c=r().profile?.(l);if(!c)return null;let g=c.displayName;return typeof g!="string"||g===""?null:{displayName:$(g,"profile.displayName"),...typeof c.displayTag=="string"&&c.displayTag!==""?{displayTag:$(c.displayTag,"profile.displayTag")}:{},...typeof c.discriminator=="string"?{discriminator:c.discriminator}:{}}},playerNumber(l){return r().playerNumber?.(l)},send(l,d,c){c===void 0?r().send(l,d):r().send(l,d,c);},broadcast(l,d){d===void 0?r().broadcast(l):r().broadcast(l,d);},kick(l,d){d===void 0?r().kick(l):r().kick(l,d);},log(l){r().log(l);},warn(l){r().warn(l);}},attach:()=>{o=D(r(),{...e,clock:t});}}}function Ae(n,e={}){let{server:t,attach:r}=K(()=>n,e);return r(),t}function Vt(n){if(n===void 0||typeof n.reason!="string"||n.reason==="")return;let e=n.platform_reason??n.endedReason;return {reason:n.reason,...e===void 0?{}:{endedReason:ve(e)}}}var Wt=1e4;function zt(n){let e=false;return t=>{if(e)return;let r;try{r=n.match.reportPlayers([{playerId:t,state:"connected"}],AbortSignal.timeout(Wt));}catch{return}r.catch(o=>{o instanceof m&&o.status>=400&&o.status<500&&(e=o.status!==429);});}}function Pe(n,e={}){return ()=>{let t=null,{server:r,attach:o}=K(()=>t,e),a=n(r),i={async init(d){t=d,o(),await a.init?.();},async shutdown(d){let c=Vt(d);try{await a.shutdown?.(c);}finally{r.dispose();}}},l=zt(r);return i.join=(d,c)=>(l(d),a.join?.(d,c)),a.leave&&(i.leave=(d,c)=>a.leave?.(d,c)),a.message&&(i.message=(d,c,g,h)=>a.message?.(d,c,g,h)),i}}var Kt=new Set(["unlocked","already_unlocked","suppressed"]);function Oe(n){let e=n?.changes;if(!Array.isArray(e))return [];let t=[];for(let r of e){let o=r??{};if(o.type!=="achievement_unlocked"||typeof o.outcome!="string"||!Kt.has(o.outcome)||typeof o.achievement_id!="string"||o.achievement_id==="")continue;let a=o.outcome;t.push({achievementId:o.achievement_id,progressionDefinitionId:typeof o.progression_definition_id=="string"?o.progression_definition_id:"",points:a==="unlocked"&&typeof o.points=="number"&&Number.isFinite(o.points)?o.points:0,outcome:a,...typeof o.reason=="string"?{reason:o.reason}:{}});}return t}export{_ as AuthError,F as CONDITIONAL_HEADERS,M as FunctionsApi,m as GemApiError,C as GemClient,q as GemFunctionError,x as PlayerText,y as TransportRefusal,Oe as achievementReceipts,$ as asPlayerText,D as createGemServer,de as createHostTokenSource,Ae as createMatchServer,Pe as defineServer,E as systemClock,Se as text,X as toBase64};//# sourceMappingURL=gem-web-sdk.server.js.map
|
|
3
3
|
//# sourceMappingURL=gem-web-sdk.server.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The function surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/function` v13.4.
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/function` v13.4.3, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/function` exports exactly the symbols on this page and nothing else.
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The browser surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk` v13.4.
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v13.4.3, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/game` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
|
@@ -7761,21 +7761,22 @@ readonly onScreenshot?: (request: { maxWidth: number; maxHeight: number }) => Pr
|
|
|
7761
7761
|
Supply the screenshot yourself, instead of letting the SDK find it.
|
|
7762
7762
|
|
|
7763
7763
|
**You do not need this to get screenshots.** By default the SDK captures
|
|
7764
|
-
your largest visible canvas, which is right for almost every game
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
picture. That is a one-line change and NOT a reason to reach for this
|
|
7770
|
-
option.
|
|
7764
|
+
your largest visible canvas, which is right for almost every game: 2D,
|
|
7765
|
+
WebGL or WebGPU, and WITHOUT `preserveDrawingBuffer`, which you should leave
|
|
7766
|
+
off. The SDK reads the canvas right after a frame you draw, or keeps the
|
|
7767
|
+
last one you drew before your loop stopped. That needs `createGem` called
|
|
7768
|
+
before your renderer is made, and drawing inside `requestAnimationFrame`.
|
|
7771
7769
|
|
|
7772
7770
|
Reach for it when the default genuinely cannot work, or when you want a say
|
|
7773
7771
|
per moment:
|
|
7774
7772
|
|
|
7775
|
-
- your canvas is an `OffscreenCanvas` driven from a worker
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7773
|
+
- your canvas is an `OffscreenCanvas` driven from a worker. Nothing on the
|
|
7774
|
+
page can read it, no flag changes that, and the platform reports
|
|
7775
|
+
`unreadable` rather than `unsupported` precisely so you do not go looking
|
|
7776
|
+
for a flag;
|
|
7777
|
+
- you draw outside `requestAnimationFrame`, so there is no moment the SDK
|
|
7778
|
+
can read your canvas in: render, then `createImageBitmap(canvas)`, in the
|
|
7779
|
+
same task;
|
|
7779
7780
|
- you draw across several layered canvases and want them composited;
|
|
7780
7781
|
- you want to decline while something specific is on screen.
|
|
7781
7782
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The server surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk` v13.4.
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v13.4.3, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/server` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 2,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "13.4.
|
|
5
|
+
"version": "13.4.3"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8790,7 +8790,7 @@
|
|
|
8790
8790
|
"group": "start",
|
|
8791
8791
|
"signature": "readonly onScreenshot?: (request: { maxWidth: number; maxHeight: number }) => Promise<ImageBitmap | null>",
|
|
8792
8792
|
"documented": true,
|
|
8793
|
-
"summary": "Supply the screenshot yourself, instead of letting the SDK find it.\n\n**You do not need this to get screenshots.** By default the SDK captures\nyour largest visible canvas, which is right for almost every game
|
|
8793
|
+
"summary": "Supply the screenshot yourself, instead of letting the SDK find it.\n\n**You do not need this to get screenshots.** By default the SDK captures\nyour largest visible canvas, which is right for almost every game: 2D,\nWebGL or WebGPU, and WITHOUT `preserveDrawingBuffer`, which you should leave\noff. The SDK reads the canvas right after a frame you draw, or keeps the\nlast one you drew before your loop stopped. That needs `createGem` called\nbefore your renderer is made, and drawing inside `requestAnimationFrame`.\n\nReach for it when the default genuinely cannot work, or when you want a say\nper moment:\n\n- your canvas is an `OffscreenCanvas` driven from a worker. Nothing on the\n page can read it, no flag changes that, and the platform reports\n `unreadable` rather than `unsupported` precisely so you do not go looking\n for a flag;\n- you draw outside `requestAnimationFrame`, so there is no moment the SDK\n can read your canvas in: render, then `createImageBitmap(canvas)`, in the\n same task;\n- you draw across several layered canvases and want them composited;\n- you want to decline while something specific is on screen.\n\n**Return `null` to decline** — a cutscene, licensed art you may not\nredistribute, another player's private board. Declining is a normal answer,\nnot an error: the player is told a picture was not available and can still\nsend their report. Nothing is logged against you for it, and returning\n`null` unconditionally is how a game opts out of screenshots entirely.\n\n```js\nonScreenshot: async ({ maxWidth, maxHeight }) => {\n if (inCutscene) return null;\n return createImageBitmap(myCanvas);\n}\n```\n\nFit the result within `maxWidth` / `maxHeight` if you can; the SDK\ndownscales for you if you overshoot.",
|
|
8794
8794
|
"deprecated": null,
|
|
8795
8795
|
"params": [],
|
|
8796
8796
|
"returns": null,
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,18 @@ 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
|
+
## 13.4.3
|
|
20
|
+
|
|
21
|
+
Upgrading from 13.4.2. Impact: none.
|
|
22
|
+
|
|
23
|
+
Nothing in this release asks anything of a game.
|
|
24
|
+
|
|
25
|
+
## 13.4.2
|
|
26
|
+
|
|
27
|
+
Upgrading from 13.4.1. Impact: none.
|
|
28
|
+
|
|
29
|
+
Nothing in this release asks anything of a game.
|
|
30
|
+
|
|
19
31
|
## 13.4.1
|
|
20
32
|
|
|
21
33
|
Upgrading from 13.4.0. Impact: none.
|
package/docs/getting-started.md
CHANGED
|
@@ -50,7 +50,7 @@ either way.
|
|
|
50
50
|
```jsonc
|
|
51
51
|
// package.json
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@indiegems/gem-web-sdk": "13.4.
|
|
53
|
+
"@indiegems/gem-web-sdk": "13.4.3"
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
@@ -149,19 +149,12 @@ reports. A `clientVersion` option still exists and is deprecated; see
|
|
|
149
149
|
[what a game must do](what-a-game-must-do.md#2-your-version-number-which-you-no-longer-declare)
|
|
150
150
|
for when an older arcade still needs it.
|
|
151
151
|
|
|
152
|
-
**
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Players can attach a screenshot to a bug report, and without that flag the browser may clear
|
|
159
|
-
your drawing buffer before the platform can read it. The SDK detects that and refuses rather
|
|
160
|
-
than sending your team **a fully black image with no error attached** — but the cost is that
|
|
161
|
-
your reports arrive with no picture. Two-dimensional canvas games need nothing here.
|
|
162
|
-
[The full section](what-a-game-must-do.md#bug-reports-players-send-you-and-the-one-webgl-flag-that-decides-them)
|
|
163
|
-
covers overriding the capture and attaching your own debug state, which is the part actually
|
|
164
|
-
worth spending effort on.
|
|
152
|
+
**Screenshots need nothing from you**, in 2D, WebGL or WebGPU, and that includes
|
|
153
|
+
`preserveDrawingBuffer`, which you should leave off. Players can attach a screenshot to a bug
|
|
154
|
+
report, and the SDK reads your canvas right after a frame you draw, so call `createGem` before you
|
|
155
|
+
create your renderer and draw inside `requestAnimationFrame`, as every engine does.
|
|
156
|
+
[The full section](what-a-game-must-do.md#bug-reports-players-send-you) covers overriding the
|
|
157
|
+
capture and attaching your own debug state, which is the part actually worth spending effort on.
|
|
165
158
|
|
|
166
159
|
**There is no offline mode.** A game must be framed by a page that speaks the bridge: opened
|
|
167
160
|
at the top level, `createGem` throws `GemUnframedError`, which says how to run it. To run framed
|
package/docs/manifest.json
CHANGED
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"file": "changelog.md",
|
|
41
|
-
"sha256": "
|
|
42
|
-
"updated": "2026-10-
|
|
41
|
+
"sha256": "90dc1aab037353e930fe1d16d8cc3f9fd6d2b60579c16146683f34fec0a0e9a1",
|
|
42
|
+
"updated": "2026-10-19T18:28:26Z"
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"file": "daily-challenges.md",
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
"file": "getting-started.md",
|
|
71
|
-
"sha256": "
|
|
72
|
-
"updated": "2026-10-
|
|
71
|
+
"sha256": "bb6a59e57e0e5498388e6c436c3b0c0949bb4c115e2e42ad8e53fbfef59eb5f7",
|
|
72
|
+
"updated": "2026-10-19T18:28:26Z"
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"file": "inventory-and-catalogs.md",
|
|
@@ -128,8 +128,8 @@
|
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
"file": "server-bundles.md",
|
|
131
|
-
"sha256": "
|
|
132
|
-
"updated": "2026-10-
|
|
131
|
+
"sha256": "028e89d4bd33272eb82631a753f10d05853fa68d81da02e3b6e2fd64758a0848",
|
|
132
|
+
"updated": "2026-10-19T18:28:26Z"
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"file": "serverless-functions-js.md",
|
|
@@ -153,8 +153,8 @@
|
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
"file": "what-a-game-must-do.md",
|
|
156
|
-
"sha256": "
|
|
157
|
-
"updated": "2026-10-
|
|
156
|
+
"sha256": "d696ecadad20b7f0e78fbc2b171fb4d767d2c6e8b9bcebc4c920432ab5799db1",
|
|
157
|
+
"updated": "2026-10-19T18:03:10Z"
|
|
158
158
|
}
|
|
159
159
|
]
|
|
160
160
|
}
|
package/docs/server-bundles.md
CHANGED
|
@@ -80,8 +80,8 @@ To check the download yourself: the package npm serves is, byte for byte, the fi
|
|
|
80
80
|
also publishes on its download host, with the hash beside it:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
curl -fsSL -O https://downloads.gemarcade.com/sdk/v13.4.
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/v13.4.
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v13.4.3/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v13.4.3/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
|
@@ -1951,34 +1951,40 @@ advice to a client you do not trust, so whatever it decides it must also carry o
|
|
|
1951
1951
|
spending currency or granting an item is a recipe, which the platform already runs where a client
|
|
1952
1952
|
cannot change it. [`serverless-functions.md`](serverless-functions.md) is the page.
|
|
1953
1953
|
|
|
1954
|
-
## Bug reports players send you
|
|
1954
|
+
## Bug reports players send you
|
|
1955
1955
|
|
|
1956
1956
|
The arcade has a **Feedback** control. A player taps it, the platform asks your game for a picture
|
|
1957
1957
|
of itself, the player draws on it and writes what happened, and it arrives on your game's feedback
|
|
1958
1958
|
list. You get this for nothing — there is no option to set.
|
|
1959
1959
|
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
`
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1960
|
+
### WebGL and WebGPU games: leave `preserveDrawingBuffer` off
|
|
1961
|
+
|
|
1962
|
+
**You do not need `preserveDrawingBuffer: true` for screenshots, and should not set it.** With it
|
|
1963
|
+
on, the browser copies the whole drawing buffer every frame instead of swapping it (measured at
|
|
1964
|
+
about 0.4 ms of GPU time a frame on a 2021 Android flagship) to keep pixels readable that the SDK
|
|
1965
|
+
only needs once, when a player files a report.
|
|
1966
|
+
|
|
1967
|
+
A WebGL canvas without the flag, and every WebGPU canvas, can be read only right after a frame is
|
|
1968
|
+
drawn: read it later and you get **a fully black image and no error**. So the SDK reads it there.
|
|
1969
|
+
It watches `requestAnimationFrame` and, when a capture is asked for, reads your canvas right after
|
|
1970
|
+
your next frame. When your game has stopped asking for frames, because nothing on screen is moving,
|
|
1971
|
+
it uses the picture it kept from the last frame you drew before you stopped. Every read is checked,
|
|
1972
|
+
and a blank one is never sent.
|
|
1973
|
+
|
|
1974
|
+
Two things keep that working, and an ordinary game does both already:
|
|
1975
|
+
|
|
1976
|
+
- **Call `createGem` before you create your renderer.** The SDK records each context as it is made,
|
|
1977
|
+
so that it never has to ask a canvas for one. Asking would create a context on a canvas you had
|
|
1978
|
+
not started yet, and take it from you.
|
|
1979
|
+
- **Draw inside `requestAnimationFrame`** (three.js's `setAnimationLoop`, Phaser, Babylon.js and
|
|
1980
|
+
PixiJS all do), **from a loop that stops by not asking for the next frame.** A frame drawn from
|
|
1981
|
+
an input handler or a timer has no moment the SDK can read it in, and a loop stopped with
|
|
1982
|
+
`cancelAnimationFrame` from outside a frame leaves no last frame to keep.
|
|
1983
|
+
|
|
1984
|
+
When neither read produced a picture, the report says so in its diagnostics: `unsupported` for
|
|
1985
|
+
WebGL (`preserveDrawingBuffer: true` would fix it, at the cost above), `unreadable` for WebGPU (only
|
|
1986
|
+
`onScreenshot` can). 2D canvas games need nothing. `OffscreenCanvas` games need `onScreenshot`,
|
|
1987
|
+
below.
|
|
1982
1988
|
|
|
1983
1989
|
### When the default capture is not right
|
|
1984
1990
|
|
package/package.json
CHANGED
package/schema/bridge.v1.json
CHANGED
|
@@ -2155,8 +2155,8 @@
|
|
|
2155
2155
|
"$comment": [
|
|
2156
2156
|
"`not_wired` -- the capability was advertised but nothing implements it.",
|
|
2157
2157
|
"`no_source` -- the default strategy found no visible canvas, or one of zero area.",
|
|
2158
|
-
"`unsupported` -- the WebGL context has `preserveDrawingBuffer: false
|
|
2159
|
-
"`unreadable` -- this page cannot read that canvas
|
|
2158
|
+
"`unsupported` -- the WebGL context has `preserveDrawingBuffer: false` and no read produced a picture (no frame was drawn while the capture waited, and none was kept when the loop last stopped), or the context was lost. THE ONE THE FLAG FIXES, and it is kept narrow on purpose: it used to also cover the case below, so a developer whose canvas simply cannot be read was told to set a flag that would change nothing.",
|
|
2159
|
+
"`unreadable` -- this page cannot read that canvas: control was transferred to an OffscreenCanvas, or a WebGPU canvas produced no picture in the frame or at rest. No flag fixes it; the game must supply `onScreenshot`.",
|
|
2160
2160
|
"`tainted` -- the canvas origin-clean flag is false. THE ONE A DEVELOPER CAN ACT ON (add `crossorigin` to the offending image), so it must stay distinguishable from `failed`.",
|
|
2161
2161
|
"`too_large` -- the source exceeds the requested caps and cannot be downscaled.",
|
|
2162
2162
|
"`busy` -- a capture is already in flight. Single-flight is enforced by the frame.",
|