@onceworlds/cli 0.9.5 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -18
- package/dist/cli.js +4608 -559
- package/dist/emulator.js +13753 -0
- package/dist/sdk.js +12 -0
- package/dist/v1/bots.js +193 -0
- package/dist/v1/host.js +547 -0
- package/dist/v1/match.js +1263 -0
- package/package.json +3 -3
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";(()=>{var pn=Object.defineProperty;var fn=(n,e,t)=>e in n?pn(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var p=(n,e,t)=>fn(n,typeof e!="symbol"?e+"":e,t);var gn=["#f6dcc5","#ebc29f","#d4a07a","#b07a55","#875839","#5e3d29","#93c9bb","#a8c595","#94bcd3","#ab9bd9","#dca3ad","#b3bccb"],Ve=["#c95f56","#dc8a4f","#e2bd52","#9cc27a","#5a8a5e","#4f9a8e","#6fb7c8","#5b87c5","#465089","#8b68a8","#dc8fae","#f2eee6","#a9a8ad","#5d5c63","#2b2a30","#80604d"],hn=["Happy","Grin","Shades","Wink","Surprised","Determined","Joyful","Shy"],yn=["None","Sprout","Cap","Beanie","Crown","Headphones","Horns","Antenna","Bow","Halo","Party hat","Cat ears","Short hair","Tuft","Bob","Spikes","Bun","Curls","Long","Mop"];var bn=["Plain","Band","Spots","Stripes","Star"];var wn={skin:gn.length,shirt:Ve.length,pants:Ve.length,face:hn.length,hat:yn.length,top:bn.length};function We(n){if(!n||typeof n!="object")return null;let e=n,t={};for(let[r,s]of Object.entries(wn)){let o=e[r],i=typeof o=="number"?o:typeof o=="string"&&/^[0-9]{1,3}$/.test(o)?Number(o):NaN;if(!Number.isInteger(i)||i<0||i>=s)return null;t[r]=i}return t}var S={roomMaxPlayersDefault:16,roomMaxPlayersCap:30,teamsMax:8,messageBytes:16*1024,presenceBytes:1024,stateKeyLength:64,stateValueBytes:16*1024,stateKeys:256,privateKeys:16,privateValueBytes:4*1024,privateBytesPerPlayer:8*1024,messagesPerSecond:60,messagesPerSecondMin:10,roomBytesPerSecond:128*1024,roomBytesPerSecondMin:16*1024,presenceHz:20,presenceHzMin:5,roomMessagesPerSecond:900,roomBytesPerSecondTotal:2*1024*1024,matchCountdownMs:3e3,matchCountdownMaxMs:1e4,matchIdleMs:45e3,matchAbandonMs:12e4,idleAfterMs:3e4,idleAbandonMs:5*6e4,rankedPairMatchesPerDay:5,chatLength:200,chatIntervalMs:750,chatHistory:50,chatChannelsPerPlayer:10,saveValueBytes:64*1024,saveKeysPerGame:100,saveBytesPerGame:1024*1024,guestSaveBytesPerGame:128*1024,serverDataKeys:128,serverDataBytes:512*1024,serverDataServersPerGame:5e3,serverDataBytesPerGame:64*1024*1024,serverDataIdleDays:120,gameDataKeys:32,gameDataListMax:100,gameDataItemBytes:1024,gameDataValueBytes:8*1024,gameDataMapFields:1e3,gameDataMapPerPlayer:100,gameDataNumberMax:1e15,gameDataWritesPerMinute:30,deployFiles:2e3,deployFileBytes:25*1024*1024,deployHtmlBytes:10*1024*1024,deployPathLength:200,deployTotalBytes:100*1024*1024,envVars:50,envValueBytes:4*1024,envTotalBytes:16*1024,proxyRequestBytes:1024*1024,proxyTextRequestBytes:256*1024,proxySpeechRequestBytes:16*1024,proxyResponseBytes:10*1024*1024,proxyMaxOutputTokens:4096,proxyCallsPerDay:2e3,proxyCallsPerDayMax:1e5,proxyCallsPerPlayerPerDay:100,proxyCallsPerNetworkPerDay:1e3,proxyMediaCallsPerDay:100,proxyMediaCallsPerDayMax:1e4,proxyMediaCallsPerPlayerPerDay:20,proxyMediaCallsPerNetworkPerDay:25,proxyUnitsPerDay:2e6,proxyUnitsPerDayMax:1e8,proxyUnitsPerPlayerPerDay:25e4,proxyUnitsPerGuestPerDay:1e5,proxyUnitsPerNetworkPerDay:5e5,errorReportsPerPage:10,consoleLineLength:2e3,consoleLinesPerSecond:50,gameNameLength:60,gameDescriptionLength:1e3,galleryItems:5,mediaImageBytes:5*1024*1024,mediaVideoBytes:30*1024*1024,mediaGifBytes:1024*1024,mediaGifSide:512,mediaUploadsPerDay:200,mediaBytesPerDay:500*1024*1024,badgesPerGame:50,controlHints:12,controlHintKeys:4,controlKeyLength:12,controlActionLength:40,helpLength:400,badgeNameLength:50,badgeDescriptionLength:200,leaderboardsPerGame:25,leaderboardTop:100,gamesPerOwner:50,gamesCreatedPerCreatorPerDay:3,uploadAccountAgeMs:10080*6e4,deploysPerOwnerPerDay:100,draftsPerOwnerPerDay:1e3,draftsKeptPerGame:3,cliTokensPerAccount:10,cliDeviceCodeSeconds:600,deploymentsKeptPerGame:10,deployBytesPerOwnerPerDay:2*1024*1024*1024,deployEntriesPerOwnerPerDay:1e5,bioLength:500,exportRows:2e3,exportSaveChars:4*1024*1024,exportEveryMs:60*6e4,friendsMax:200,friendRequestsMax:100,friendRequestsIncomingMax:50,friendRequestsPerDay:20,friendRequestsPerNetworkPerDay:120,friendRequestPushesPerDay:5,passwordMinLength:8,passwordMaxLength:72,signupsPerNetworkPerDay:100,signupsPerWidePerDay:300,guestsPerNetworkPerDay:300,guestsPerWidePerDay:1500,visitsPerNetworkPerDay:10,visitSeconds:10,errorsPerDeployment:100,controlButtons:4,controlLabelLength:12,partySize:6,partyChatHistory:50,messageLength:500,messagesPerPage:50,groupChatMembers:20,groupChatNameLength:40,conversationsListed:50,notificationsListed:50,invitesPerRequest:10,inviteLiveMs:30*6e4,partyInviteMs:60*6e4};var kn="host:",H="data:";function J(n){return n.startsWith(kn)||n.startsWith(H)}var Ye=/^(?!__proto__$)[A-Za-z0-9_.:-]{1,59}$/;function ke(n){return n.startsWith(H)}function ve(n){return H+n}function Qe(n){return n.startsWith("private:")||n.startsWith("room:")}function Se(n){let e=n.length;for(let t=0;t<n.length;t++){let r=n.charCodeAt(t);r<128||(r<2048?e+=1:r>=55296&&r<=56319&&t+1<n.length&&(n.charCodeAt(t+1)&64512)===56320?(e+=2,t++):e+=2)}return e}var xe=/^[A-Za-z][A-Za-z0-9_.-]{0,31}$/;var vn={counter:["add"],list:["push"],record:["best"],map:["claim","release"],value:["set"]},Sn={add:"add(key, n)",push:"push(key, item)",best:"best(key, n)",claim:"claim(key, field)",release:"release(key, field)",set:"set(key, value)"};function xn(n){return vn[n].map(e=>`ow.data.game.${Sn[e]}`).join(" or ")}function Me(n,e){let r=Math.floor(e/864e5);if(n==="daily")return{start:r*864e5,next:(r+1)*864e5};let s=r-(r+3)%7;return{start:s*864e5,next:(s+7)*864e5}}function Mn(n,e){switch(n.type){case"counter":return{type:"counter",value:n.min,...n.reset?{resetsAt:Me(n.reset,e).next}:{}};case"list":return{type:"list",items:[]};case"record":return{type:"record",value:null,by:null,at:null};case"map":return{type:"map",fields:{}};case"value":return{type:"value",value:null,at:null}}}function Rn(n,e,t){if(!e||e.type!==n.type)return Mn(n,t);if(e.type==="counter"&&n.type==="counter"){if(!n.reset)return{type:"counter",value:e.value};let o=Me(n.reset,t);return{type:"counter",value:(e.period??0)>=o.start?e.value:n.min,resetsAt:o.next}}let{period:r,...s}=e;return s}function Ze(n,e,t,r,s){if(r.guest&&!n.guests)return{code:"sign_in",message:`Log in to change '${t.key}': guests can't (a ${n.type} is for signed-in players unless onceworlds.json says "guests": true).`};let o={id:r.id,name:r.name},i=Rn(n,e,s);switch(i.type){case"counter":{if(t.op!=="add"||n.type!=="counter")break;let l=Math.min(n.max,Math.max(n.min,i.value+t.n)),c=n.reset?Me(n.reset,s):null;return{next:{type:"counter",value:l,...c?{resetsAt:c.next,period:c.start}:{}},result:{type:"counter",value:l}}}case"list":{if(t.op!=="push"||n.type!=="list")break;return{next:{type:"list",items:[{value:t.item,by:o,at:s},...i.items].slice(0,n.max)},result:{type:"list",ok:!0}}}case"record":{if(t.op!=="best"||n.type!=="record")break;return i.value!==null&&i.by&&i.at!==null&&!(n.order==="max"?t.n>i.value:t.n<i.value)?{next:i,result:{type:"record",value:i.value,by:i.by,at:i.at,record:!1}}:{next:{type:"record",value:t.n,by:o,at:s},result:{type:"record",value:t.n,by:o,at:s,record:!0}}}case"map":{if(t.op!=="claim"&&t.op!=="release"||n.type!=="map")break;let l={...i.fields},c=Object.hasOwn(l,t.field)?l[t.field]:void 0;return t.op==="release"?!c||c.by.id!==r.id?{next:i,result:{type:"map",ok:!1,...c?{holder:c.by}:{}}}:(delete l[t.field],{next:{type:"map",fields:l},result:{type:"map",ok:!0}}):c?{next:i,result:{type:"map",ok:c.by.id===r.id,holder:c.by}}:Object.values(l).filter(d=>d.by.id===r.id).length>=n.perPlayer?{code:"limit",message:`claim('${t.key}'): a player holds at most ${n.perPlayer} (onceworlds.json's "perPlayer"). Release one first.`}:Object.keys(l).length>=n.fields?{code:"full",message:`claim('${t.key}'): all ${n.fields} fields are taken (onceworlds.json's "fields").`}:(l[t.field]={by:o,at:s},{next:{type:"map",fields:l},result:{type:"map",ok:!0}})}case"value":{if(t.op!=="set"||n.type!=="value")break;return r.host?{next:{type:"value",value:t.value,at:s},result:{type:"value",ok:!0}}:{code:"not_host",message:`set('${t.key}'): only the host of one of the game's rooms sets a value. Join a room, and set it from the host's page (room.isHost).`}}}return{code:"wrong_op",message:`'${t.key}' is a ${n.type}: use ${xn(n.type)}.`}}var Je=n=>n.replace(/[\u0000-\u001f\u007f]/g," ").replace(/\s+/g," ").trim();var En=/^[a-z0-9][a-z0-9-]{0,19}$/;function Xe(n){if(!n||typeof n!="object")return null;let e=n,t=[];for(let s of Array.isArray(e.buttons)?e.buttons:[]){if(t.length>=S.controlButtons)break;let{id:o,label:i}=s??{};if(typeof o!="string"||!En.test(o)||t.some(c=>c.id===o))continue;let l=typeof i=="string"?i.replace(/[\u0000-\u001f\u007f]/g,"").replace(/\s+/g," ").trim().slice(0,S.controlLabelLength).trim():"";t.push({id:o,label:l||o})}let r=e.stick===!0;return r||t.length>0?{stick:r,zone:e.zone==="corner"?"corner":"wide",buttons:t}:null}var Y="host:ow.settings",Pn=/^[a-z][a-zA-Z0-9]{0,15}$/;function Re(n){if(!Array.isArray(n))return[];let e=[];for(let t of n){if(e.length>=6)break;let{id:r,label:s,options:o,default:i}=t??{};if(typeof r!="string"||!Pn.test(r)||e.some(d=>d.id===r)||!Array.isArray(o))continue;let l=[];for(let d of o){let m=typeof d=="object"&&d!==null?d.value:d;if(typeof m!="string"&&typeof m!="number"||typeof m=="number"&&!Number.isFinite(m)||l.length>=8||l.some(f=>f.value===m))continue;let h=typeof d=="object"&&d!==null&&typeof d.label=="string"?d.label:String(m);l.push({value:m,label:Je(h).slice(0,20)||String(m)})}if(l.length<2)continue;let c=typeof s=="string"?Je(s).slice(0,24):"";e.push({id:r,label:c||r,options:l,default:l.some(d=>d.value===i)?i:l[0].value})}return e}function et(n,e){let t=e!==null&&typeof e=="object"?e[n.id]:void 0;return n.options.some(r=>r.value===t)?t:n.default}var tt="onceworlds:hello",nt="onceworlds:connect",rt=/^[a-z0-9]{1,32}~(?:(?:0|[1-9]\d{0,3})\.(?:0|[1-9]\d{0,4})\.(?:0|[1-9]\d{0,5}))?~[0-9a-z]{1,11}~[lp]~[A-Za-z0-9_-]{43}$/;var ot=["log","info","warn","error"],st="onceworlds-console";var it=/^[A-Za-z0-9-]{1,64}$/;function at(){let n=1,e=!1,t=()=>e?0:n,r=new Map;if(typeof AudioNode<"u"&&typeof AudioDestinationNode<"u"){let d=AudioNode.prototype.connect,m=AudioNode.prototype.disconnect,h=w=>typeof OfflineAudioContext>"u"||!(w instanceof OfflineAudioContext),f=w=>{let y=r.get(w);return y||(y=w.createGain(),y.gain.value=t(),d.call(y,w.destination),r.set(w,y)),y};AudioNode.prototype.connect=function(w,...y){return w instanceof AudioDestinationNode&&h(w.context)&&r.get(w.context)!==this?(d.call(this,f(w.context),...y),w):d.call(this,w,...y)},AudioNode.prototype.disconnect=function(...w){let y=w[0];if(y instanceof AudioDestinationNode){let k=r.get(y.context);k&&k!==this&&(w[0]=k)}return m.apply(this,w)}}let s=new WeakMap,o=new Set,i=typeof HTMLMediaElement<"u"?Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype,"volume"):void 0,l=d=>i?.set?.call(d,Ee((s.get(d)??1)*t())),c=d=>{s.has(d)||!i?.get||(s.set(d,i.get.call(d)),o.add(new WeakRef(d)))};if(i?.get&&i.set){Object.defineProperty(HTMLMediaElement.prototype,"volume",{configurable:!0,enumerable:i.enumerable,get(){return s.has(this)?s.get(this):i.get.call(this)},set(m){c(this),s.set(this,Ee(Number(m))),l(this)}});let d=HTMLMediaElement.prototype.play;HTMLMediaElement.prototype.play=function(){return c(this),l(this),d.call(this)}}return{get volume(){return n},get muted(){return e},apply(d){n=Ee(d.volume),e=d.muted;for(let[m,h]of r)h.gain.setTargetAtTime(t(),m.currentTime,.02);for(let m of o){let h=m.deref();h?l(h):o.delete(m)}}}}function Ee(n){return Number.isFinite(n)?Math.min(1,Math.max(0,n)):1}function lt(n){try{let e=n?.getAttribute("data-build");return e&&rt.test(e)?e:null}catch{return null}}var O={ms:9e3,warmupMs:1e3,flowSettleMs:1500,playSettleMs:2e3,motionGraceMs:5e3,ceilingMs:25e3,graceMs:12e3,maxBytes:6*1024*1024,posterBytes:1024*1024,long:1280,short:720,fps:30,bitsPerSecond:25e5};var $n=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm","video/mp4;codecs=avc1","video/mp4"],Cn=3e3,ct=250,Ln="host:ow.flow";function ut(n){let e=n?.[Ln],t=e&&typeof e=="object"?e.phase:void 0;return typeof t=="string"?t:void 0}var Pe=250,oe=32,se=18,_n=.02,Ae={needed:6,of:8};function Dn(){return typeof window>"u"||typeof document>"u"?null:{document,MediaRecorder:globalThis.MediaRecorder,requestAnimationFrame:n=>window.requestAnimationFrame(n),setTimeout:(n,e)=>window.setTimeout(n,e),clearTimeout:n=>window.clearTimeout(n)}}var F=(n,e,t,r)=>typeof n=="number"&&Number.isFinite(n)?Math.min(t,Math.max(e,Math.round(n))):r;function On(n){if(!n||typeof n!="object")return null;let e=n;return e.t==="stop"?{t:"stop"}:e.t!=="start"?null:{t:"start",ms:F(e.ms,1e3,15e3,O.ms),long:F(e.long,160,1920,O.long),short:F(e.short,90,1080,O.short),fps:F(e.fps,5,60,O.fps),bitsPerSecond:F(e.bitsPerSecond,25e4,8e6,O.bitsPerSecond),maxBytes:F(e.maxBytes,64*1024,O.maxBytes,O.maxBytes)}}function ie(n){let e=null,t=0;for(let r of Array.from(n))try{if(r.isConnected===!1||r.width<64||r.height<64)continue;let s=typeof r.getBoundingClientRect=="function"?r.getBoundingClientRect():null;if(s&&(s.width<32||s.height<32))continue;let o=s?s.width*s.height:r.width*r.height;o>t&&(e=r,t=o)}catch{}return e}function Tn(n,e,t,r){let s=n>=e,o=Math.min(1,(s?t:r)/n,(s?r:t)/e);if(o>=1)return{width:n,height:e,scale:1};let i=l=>Math.max(2,Math.floor(l*o/2)*2);return{width:i(n),height:i(e),scale:o}}function In(n,e){return new Promise(t=>{e.requestAnimationFrame(()=>{try{let r=n.width,s=n.height,o=Math.min(O.long,Math.max(480,r)),i=Math.round(o*9/16),l=e.document.createElement("canvas");l.width=o,l.height=i;let c=l.getContext("2d");if(!c)return t(null);c.fillStyle="#000",c.fillRect(0,0,o,i);let d=r/s,m=d>=1.25&&d<=2.4?Math.max(o/r,i/s):Math.min(o/r,i/s);c.drawImage(n,(o-r*m)/2,(i-s*m)/2,r*m,s*m),l.toBlob(h=>h?h.arrayBuffer().then(t,()=>t(null)):t(null),"image/jpeg",.85)}catch{t(null)}})})}function Bn(n,e){let t=null,r=null;return s=>{e.requestAnimationFrame(()=>{try{if(!t){let c=e.document.createElement("canvas");c.width=oe,c.height=se,t=c.getContext("2d",{willReadFrequently:!0})}if(!t)return s(null);t.drawImage(n,0,0,oe,se);let o=t.getImageData(0,0,oe,se).data,i=r;if(r=new Uint8ClampedArray(o),!i)return s(null);let l=0;for(let c=0;c+2<o.length;c+=4){let d=o[c]*2+o[c+1]*5+o[c+2],m=i[c]*2+i[c+1]*5+i[c+2];Math.abs(d-m)>128&&l++}s(l/(oe*se))}catch{s(null)}})}}function Nn(n,e,t,r){return new Promise(s=>{let o=Bn(n,e),i=[],l=0,c=0,d=!1,m=()=>{if(r()||l>=O.ceilingMs)return s(!1);let h=null;try{h=t()}catch{}h&&h.phase!=="lobby"&&(d=!0);let f=h?.flow;if(h?.phase==="playing"&&h.paused!==!0&&(f===void 0||f==="playing")){if(c+=Pe,c>(f===void 0?O.playSettleMs:O.flowSettleMs))return s(!0)}else c=0;if(!d){if(l>=O.motionGraceMs&&i.length>=Ae.of&&i.filter(Boolean).length>=Ae.needed)return s(!0);o(w=>{i.push(w!==null&&w>=_n),i.length>Ae.of&&i.shift()})}l+=Pe,e.setTimeout(m,Pe)};m()})}function dt(n,e){let t=e.MediaRecorder;return typeof n.captureStream!="function"||typeof t!="function"?null:$n.find(r=>{try{return t.isTypeSupported(r)}catch{return!1}})??null}function mt(n,e=Dn(),t=()=>null){let r=null,s=l=>{try{n(l)}catch{}},o=l=>s({t:"none",reason:l});function i(l){if(r)return;if(!e)return o("unsupported");let c=e,d=!1,m=null;r={cancel(){d=!0,m!==null&&c.clearTimeout(m)}};let h=y=>{r=null,!d&&y&&s(y)},f=0,w=()=>{if(d)return h(null);let y=ie(c.document.querySelectorAll("canvas"));if(y){if(m=null,!dt(y,c))return h({t:"none",reason:"unsupported"});Nn(y,c,t,()=>d).then(k=>{if(d)return null;if(!k)return{t:"none",reason:"not-playing"};let E=ie(c.document.querySelectorAll("canvas"))??y;return Gn(E,l,c,()=>d,()=>!d&&s({t:"started"}))}).then(h,()=>h({t:"none",reason:"failed"}))}else++f>Cn/ct?h({t:"none",reason:"no-canvas"}):m=c.setTimeout(w,ct)};w()}return{receive(l){try{let c=On(l);if(!c)return;if(c.t==="stop"){r?.cancel(),r=null;return}i(c)}catch{r=null,o("failed")}}}}function Gn(n,e,t,r,s){let o=t.MediaRecorder,i=dt(n,t);if(!o||!i)return Promise.resolve({t:"none",reason:"unsupported"});let l=Tn(n.width,n.height,e.long,e.short),c=!1,d=n;if(l.scale<1){let f=t.document.createElement("canvas");f.width=l.width,f.height=l.height;let w=f.getContext("2d");if(!w)return Promise.resolve({t:"none",reason:"unsupported"});c=!0;let y=()=>{if(c){try{w.drawImage(n,0,0,l.width,l.height)}catch{}t.requestAnimationFrame(y)}};y(),d=f}let m,h;try{m=d.captureStream(e.fps),h=new o(m,{mimeType:i,videoBitsPerSecond:e.bitsPerSecond})}catch{return c=!1,Promise.resolve({t:"none",reason:"failed"})}return new Promise(f=>{let w=[],y=0,k=Promise.resolve(null),E=t.setTimeout(()=>{k=In(n,t)},Math.round(e.ms/2)),_=null,T=()=>{try{h.state!=="inactive"&&h.stop()}catch{}},M=()=>{c=!1,t.clearTimeout(E),_!==null&&t.clearTimeout(_);try{for(let L of m.getTracks())L.stop()}catch{}};h.ondataavailable=L=>{!L.data||L.data.size===0||(w.push(L.data),y+=L.data.size,y>e.maxBytes*.85&&T())},h.onerror=()=>{M(),f(r()?null:{t:"none",reason:"failed"})},h.onstop=()=>{if(M(),r())return f(null);let L=new Blob(w,{type:h.mimeType||i});Promise.all([L.arrayBuffer(),k.catch(()=>null)]).then(([U,N])=>{if(U.byteLength===0)return f({t:"none",reason:"failed"});if(U.byteLength>e.maxBytes)return f({t:"none",reason:"too-large"});f({t:"done",type:L.type||i,video:U,poster:N,width:l.width,height:l.height})}).catch(()=>f({t:"none",reason:"failed"}))};try{h.start(1e3)}catch{return M(),f({t:"none",reason:"failed"})}s(),_=t.setTimeout(()=>{_=null,T()},e.ms);let P=()=>{if(h.state!=="inactive"){if(r())return T();t.setTimeout(P,200)}};t.setTimeout(P,200)})}var jn=200,pt=2,K=20,ft=200;function gt(n){try{return new URLSearchParams(n).get(st)==="1"}catch{return!1}}function ht(n){return typeof n=="object"&&n!==null&&n.solo===!0?"standalone":"platform"}function yt(n){return typeof n=="object"&&n!==null&&n.demo===!0}function bt(n){try{let e=new URLSearchParams(n).get("demo");return e!==null&&e!=="0"&&e!=="false"}catch{return!1}}function wt(n,e,t=Date.now,r=setTimeout){let s=null,o=[],i=-1/0,l=0,c=0,d=!1,m=!1,h=y=>{s?s(y):o.length<jn&&o.push(y)},f=()=>{let y=t();return y-i>=1e3&&(i=y,l=0),l<S.consoleLinesPerSecond?(l++,!0):(c++,d||(d=!0,r(()=>{d=!1;let k=c;c=0,k>0&&f()&&h({level:"warn",text:`${k} more ${k===1?"line":"lines"} left out`,at:t()})},Math.max(0,1e3-(y-i)))),!1)},w=(y,k,E)=>{if(!f())return;let _=kt(e?k.split(`${e}/`).join(""):k);h({level:y,text:_,at:t(),...E?{source:E.source.slice(0,300),line:E.line,column:E.column}:{}})};for(let y of ot){let k=n[y];typeof k=="function"&&(n[y]=function(...E){if(k.apply(n,E),!m){m=!0;try{w(y,Hn(E))}catch{}finally{m=!1}}})}return{error(y,k){if(!m){m=!0;try{w("error",y,k)}catch{}finally{m=!1}}},connect(y){s=y;for(let k of o.splice(0))y(k)}}}function Hn(n){let e="",t=n;if(typeof n[0]=="string"){let r=1;e=n[0].replace(/%[sdifoOc%]/g,s=>{if(s==="%%")return"%";if(r>=n.length)return s;let o=n[r++];return s==="%c"?"":s==="%d"||s==="%i"?String(typeof o=="number"?Math.trunc(o):parseInt(Q(o),10)):s==="%f"?String(typeof o=="number"?o:parseFloat(Q(o))):s==="%s"?Q(o):V(o)}),t=n.slice(r)}for(let r of t)e+=e?` ${Q(r)}`:Q(r);return kt(e)}function Q(n){return typeof n=="string"?n:V(n)}function V(n,e=0){try{switch(typeof n){case"string":return JSON.stringify(n.length>ft?`${n.slice(0,ft)}\u2026`:n);case"bigint":return`${n}n`;case"symbol":return n.toString();case"function":return`\u0192 ${n.name||"anonymous"}()`;case"object":break;default:return String(n)}if(n===null)return"null";if(n instanceof Error)return ae(n);if(n instanceof Date)return Number.isNaN(n.getTime())?"Invalid Date":n.toISOString();if(n instanceof RegExp)return String(n);if(n instanceof Map||n instanceof Set)return`${n.constructor.name}(${n.size})`;if(ArrayBuffer.isView(n))return`${n.constructor.name}(${"length"in n?n.length:n.byteLength})`;if(typeof Element<"u"&&n instanceof Element)return`<${n.tagName.toLowerCase()}>`;if(Array.isArray(n))return e>=pt?`Array(${n.length})`:`[${n.slice(0,K).map(i=>V(i,e+1)).join(", ")}${n.length>K?`, \u2026${n.length-K} more`:""}]`;let t=typeof n.constructor=="function"&&n.constructor!==Object?`${n.constructor.name} `:"";if(e>=pt)return`${t}{\u2026}`;let r=Object.keys(n),s=r.slice(0,K).map(o=>`${/^[A-Za-z_$][\w$]*$/.test(o)?o:JSON.stringify(o)}: ${V(n[o],e+1)}`);return`${t}{${s.join(", ")}${r.length>K?`, \u2026${r.length-K} more`:""}}`}catch{return Object.prototype.toString.call(n)}}function ae(n){try{let e=`${n.name||"Error"}: ${n.message}`,t=typeof n.stack=="string"?n.stack:"";return t?t.startsWith(e)?t:`${e}
|
|
2
|
+
${t}`:e}catch{return"Error"}}function kt(n){return n.length>S.consoleLineLength?`${n.slice(0,S.consoleLineLength-1)}\u2026`:n}var qn={wasd:{up:"w",down:"s",left:"a",right:"d"},arrows:{up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight"}},zn=.45,Un=.3,$e={" ":["Space",32],Enter:["Enter",13],Shift:["ShiftLeft",16],Control:["ControlLeft",17],Alt:["AltLeft",18],Escape:["Escape",27],Tab:["Tab",9],Backspace:["Backspace",8],ArrowLeft:["ArrowLeft",37],ArrowUp:["ArrowUp",38],ArrowRight:["ArrowRight",39],ArrowDown:["ArrowDown",40]},le=class{constructor(e,t){p(this,"touch");p(this,"stick",{x:0,y:0});p(this,"held",new Set);p(this,"downs",new Map);p(this,"stickMode");p(this,"buttonKeys",new Map);p(this,"down",new Map);p(this,"show");p(this,"reportTouch");this.show=e,this.reportTouch=t,this.touch=typeof matchMedia=="function"&&matchMedia("(pointer: coarse)").matches,window.addEventListener("pointerdown",r=>r.isTrusted&&this.setTouch(r.pointerType!=="mouse"),!0),window.addEventListener("keydown",r=>r.isTrusted&&this.setTouch(!1),!0),window.addEventListener("blur",()=>this.release()),document.addEventListener("visibilitychange",()=>document.hidden&&this.release())}set(e){let t=e??{};this.stickMode=t.stick==="wasd"||t.stick==="arrows"||t.stick==="analog"?t.stick:void 0;let r=Array.isArray(t.buttons)?t.buttons:[],s=e?Xe({stick:this.stickMode!==void 0,zone:t.zone,buttons:r}):null;this.buttonKeys=new Map;for(let o of s?.buttons??[]){let i=r.find(l=>l?.id===o.id)?.key;typeof i=="string"&&i&&this.buttonKeys.set(o.id,i)}this.show(s),s?this.update():this.release()}receive(e){let t=s=>typeof s=="number"&&Number.isFinite(s)?Math.max(-1,Math.min(1,s)):0;this.stick.x=t(e?.x),this.stick.y=t(e?.y);let r=new Set(Array.isArray(e?.buttons)?e.buttons.filter(s=>typeof s=="string"):[]);for(let s of r)this.held.has(s)||this.downs.set(s,(this.downs.get(s)??0)+1);this.held=r,this.update()}pressed(e){return this.held.has(e)}presses(e){return this.downs.get(e)??0}update(){let e=new Map;if(this.stickMode==="wasd"||this.stickMode==="arrows"){let t=qn[this.stickMode],r=(s,o)=>{o>(this.down.has(s)?Un:zn)&&e.set(s,vt(s))};r(t.up,-this.stick.y),r(t.down,this.stick.y),r(t.left,-this.stick.x),r(t.right,this.stick.x)}for(let t of this.held){let r=this.buttonKeys.get(t);r&&e.set(r,vt(r))}for(let[t,r]of this.down)e.has(t)||(this.down.delete(t),St(r,!1));for(let[t,r]of e)this.down.has(t)||(this.down.set(t,r),St(r,!0))}release(){this.stick.x=0,this.stick.y=0,this.held.clear(),this.update()}setTouch(e){e!==this.touch&&(this.touch=e,e||this.release(),this.reportTouch(e))}};function vt(n){let e=n==="Space"?" ":n;return Object.prototype.hasOwnProperty.call($e,e)?{key:e,code:$e[e][0],keyCode:$e[e][1]}:/^[a-z]$/i.test(e)?{key:e.toLowerCase(),code:`Key${e.toUpperCase()}`,keyCode:e.toUpperCase().charCodeAt(0)}:/^[0-9]$/.test(e)?{key:e,code:`Digit${e}`,keyCode:48+Number(e)}:{key:e,code:"",keyCode:0}}function St(n,e){try{let t={key:n.key,code:n.code,keyCode:n.keyCode,which:n.keyCode,bubbles:!0,cancelable:!0},r=new KeyboardEvent(e?"keydown":"keyup",t);r.keyCode!==n.keyCode&&(Object.defineProperty(r,"keyCode",{get:()=>n.keyCode}),Object.defineProperty(r,"which",{get:()=>n.keyCode})),(document.activeElement??document.body??document).dispatchEvent(r)}catch{}}function Fn(n){switch(n.type){case"counter":return n.value;case"list":return n.items;case"record":return n.value===null?null:{value:n.value,by:n.by,at:n.at};case"map":return n.fields;case"value":return n.value}}function xt(n=e=>console.warn(`[onceworlds] ${e}`)){let e=new Map;return t=>{let r=Date.now();r-(e.get(t)??-1/0)<5e3||(e.size>200&&e.clear(),e.set(t,r),n(t))}}function Mt(n,e){let t=new Set,r=async(s,o)=>{let i=`onceworlds.data.server.${o===null?"delete":"set"}('${String(s).slice(0,40)}')`,l=n();if(typeof s!="string"||!Ye.test(s))return e(`${i} did nothing: a key is 1-59 letters, digits or _ . : -`),!1;if(!l||l.closed)return e(`${i} did nothing: you're not in a room. Join one first (await onceworlds.rooms.join({ private: true })): server data belongs to a server.`),!1;if(!l.connected)return e(`${i} did nothing: the connection is down and the platform is reconnecting. Write it again after room.on('reconnect').`),!1;if(!l.isHost)return e(`${i} did nothing: only the room's host writes server data. Check onceworlds.data.server.canWrite, or send the change to the host (room.send) and let it write.`),!1;let c;try{c=JSON.stringify(o??null)}catch{c=void 0}return c===void 0?(e(`${i} did nothing: the value must be plain JSON data (numbers, text, lists, objects).`),!1):Se(c)>S.stateValueBytes-128?(e(`${i} did nothing: the value is ${Math.ceil(Se(c)/1024)} KB, and a key holds 16 KB. Split it into keys by area or chunk (a server keeps ${S.serverDataKeys} keys, ${S.serverDataBytes/1024} KB), or let the engine's Persist() save the world for you.`),!1):await l._writeAcked(ve(s),o??null)};return{get(s){let o=n();if(!o||typeof s!="string")return null;let i=o.state[ve(s)];return i===void 0?null:i},all(){let s=n(),o={};if(!s)return o;for(let[i,l]of Object.entries(s.state))i.startsWith("data:")&&(o[i.slice(5)]=l);return o},set:(s,o)=>r(s,o??null),delete:s=>r(s,null),get kept(){return n()?.kept??!1},get canWrite(){let s=n();return!!(s&&!s.closed&&s.isHost)},async reset(){let s=n();return s?s.owner!==s.me.id&&s.kind!=="solo"?(e("onceworlds.data.server.reset() did nothing: only a private server's owner starts its world over (room.owner)."),!1):s._resetData():(e("onceworlds.data.server.reset() did nothing: you're not in a room."),!1)},on(s,o){return s!=="change"||typeof o!="function"?()=>{}:(t.add(o),()=>t.delete(o))},_changed(s,o,i){for(let l of t)try{l(s,o??null,i)}catch(c){console.error('[onceworlds] error in data.server "change" listener',c)}}}}function Rt(n,e){let t=async(s,o,i,l)=>{if(typeof s.key!="string"||!xe.test(s.key))return e(`${o} did nothing: '${String(s.key).slice(0,40)}' isn't a game data key (1-32 letters, digits, _ . or -, declared in onceworlds.json's "data").`),l;try{return i(await(await n()).op(s))}catch(c){return e(`${o} did nothing: ${c instanceof Error?c.message:String(c)}`),l}},r=(s,o)=>`onceworlds.data.game.${s}('${String(o).slice(0,40)}')`;return{async get(s){if(typeof s!="string"||!xe.test(s))return e(`${r("get",s)}: '${String(s).slice(0,40)}' isn't a game data key (1-32 letters, digits, _ . or -).`),null;try{return Fn(await(await n()).read(s))}catch(o){return e(`${r("get",s)} read nothing: ${o instanceof Error?o.message:String(o)}`),null}},add:(s,o)=>t({key:s,op:"add",n:Number(o)},r("add",s),i=>i.type==="counter"?i.value:null,null),push:(s,o)=>t({key:s,op:"push",item:o},r("push",s),i=>i.type==="list",!1),best:(s,o)=>t({key:s,op:"best",n:Number(o)},r("best",s),i=>i.type==="record"?{value:i.value,by:i.by,at:i.at,record:i.record}:null,null),claim:(s,o)=>t({key:s,op:"claim",field:String(o)},r("claim",s),i=>i.type==="map"&&i.ok,!1),release:(s,o)=>t({key:s,op:"release",field:String(o)},r("release",s),i=>i.type==="map"&&i.ok,!1),set:(s,o)=>t({key:s,op:"set",value:o},r("set",s),i=>i.type==="value",!1)}}function Et(n,e){let t=S.gameDataNumberMax,r=o=>{switch(o.op){case"add":return{type:"counter",min:0,max:t,add:[-t,t],guests:!0};case"push":return{type:"list",max:S.gameDataListMax,bytes:S.gameDataItemBytes,guests:!0};case"best":return{type:"record",order:"max",min:-t,max:t,guests:!0};case"claim":case"release":return{type:"map",fields:S.gameDataMapFields,perPlayer:S.gameDataMapPerPlayer,guests:!0};case"set":return{type:"value",bytes:S.gameDataValueBytes,guests:!0}}},s=o=>{try{let i=e.get(`onceworlds:game:${o}`);return i?JSON.parse(i):null}catch{return null}};return{async read(o){let i=s(o);if(!i)throw new Error(`nothing is stored under '${o}' on this page yet (on its own, game data lives in this browser).`);return i},async op(o){let i=await n(),l=s(o.key),c=r(o);if(l&&l.type!==c.type)throw new Error(`'${o.key}' holds a ${l.type} on this page: it was first used that way.`);let d=Ze(c,l,JSON.parse(JSON.stringify(o??null)),{id:i.id,name:i.name,host:!0},Date.now());if("code"in d)throw new Error(d.message);return e.set(`onceworlds:game:${o.key}`,JSON.stringify(d.next)),d.result}}}function Pt(n){return{messagesPerSecond:n.messagesPerSecond*.85,messageBurst:n.messagesPerSecond*2*.85,bytesPerSecond:n.bytesPerSecond*.85,byteBurst:n.bytesPerSecond*4*.85}}var Kn=60,Vn=100,Wn=5e3,ce=class{constructor(e={}){p(this,"queue",[]);p(this,"rates",Pt({messagesPerSecond:S.messagesPerSecond,bytesPerSecond:S.roomBytesPerSecond}));p(this,"messages",this.rates.messageBurst);p(this,"bytes",this.rates.byteBurst);p(this,"budget",{messagesPerSecond:S.messagesPerSecond,bytesPerSecond:S.roomBytesPerSecond});p(this,"refilledAt");p(this,"timer",null);p(this,"warnedAt",-1/0);p(this,"dropped",0);p(this,"now");p(this,"setTimer");p(this,"clearTimer");p(this,"warn");this.now=e.now??Date.now,this.setTimer=e.setTimer??((t,r)=>setTimeout(t,r)),this.clearTimer=e.clearTimer??(t=>clearTimeout(t)),this.warn=e.warn??(t=>console.warn(`[onceworlds] ${t}`)),this.refilledAt=this.now(),e.budget&&this.setBudget(e.budget)}setBudget(e){this.refill(),this.budget={messagesPerSecond:e.messagesPerSecond,bytesPerSecond:e.bytesPerSecond},this.rates=Pt(this.budget),this.messages=Math.min(this.messages,this.rates.messageBurst),this.bytes=Math.min(this.bytes,this.rates.byteBurst),this.drain(!1)}get waiting(){return this.queue.length}event(e,t){this.queue.push({kind:"event",size:e,send:t}),this.settle()}urgent(e,t){this.queue.push({kind:"event",size:e,send:t}),this.drain(!0)}state(e,t,r){for(let s=this.queue.length-1;s>=0;s--){let o=this.queue[s];if(o.kind!=="state")break;if(o.key===e)return o.size=t,o.send=r,this.settle()}this.queue.push({kind:"state",key:e,size:t,send:r}),this.settle()}spend(e){this.refill(),this.bytes-=e}clear(){this.queue=[],this.timer!==null&&this.clearTimer(this.timer),this.timer=null}settle(){this.queue.length>this.waitingLimit(Kn,12)&&(this.squeeze(),this.tell(`${this.queue.length} messages are waiting: a room takes about ${this.budget.messagesPerSecond} messages and ${Math.round(this.budget.bytesPerSecond/1024)} KB a second from each player. State is sent at its newest value and other messages in order. Send less: batch events, write state at 10-20 Hz, and only what changed.`)),this.drain(!1)}waitingLimit(e,t){return Math.max(t,Math.round(e*this.rates.messagesPerSecond/(S.messagesPerSecond*.85)))}refill(){let e=this.now(),t=Math.max(0,(e-this.refilledAt)/1e3);this.refilledAt=e,this.messages=Math.min(this.rates.messageBurst,this.messages+t*this.rates.messagesPerSecond),this.bytes=Math.min(this.rates.byteBurst,this.bytes+t*this.rates.bytesPerSecond)}drain(e){for(this.timer!==null&&(this.clearTimer(this.timer),this.timer=null),this.refill();this.queue.length>0;){let i=this.queue[0],l=Math.min(i.size(),this.rates.byteBurst);if(!e&&(this.messages<1||this.bytes<l))break;this.queue.shift(),this.messages-=1,this.bytes-=l;try{i.send()}catch(c){console.error("[onceworlds] a message could not be sent",c)}}if(this.queue.length===0)return;let t=Math.min(this.queue[0].size(),this.rates.byteBurst),r=Math.max(0,1-this.messages)/this.rates.messagesPerSecond,s=Math.max(0,t-this.bytes)/this.rates.bytesPerSecond,o=Math.min(250,Math.max(8,Math.ceil(Math.max(r,s)*1e3)));this.timer=this.setTimer(()=>{this.timer=null,this.drain(!1)},o)}squeeze(){let e=new Set;for(let s=this.queue.length-1;s>=0;s--){let o=this.queue[s];o.kind==="state"&&(e.has(o.key)?this.queue.splice(s,1):e.add(o.key))}let t=this.queue.filter(s=>s.kind==="event").length,r=this.waitingLimit(Vn,20);for(let s=0;t>r&&s<this.queue.length;)this.queue[s].kind==="event"?(this.queue.splice(s,1),t--,this.dropped++):s++;this.dropped>0&&this.tell(`${this.dropped} messages were dropped so the rest arrive fresh: the game sends much more than a room takes (${this.budget.messagesPerSecond} messages, ${Math.round(this.budget.bytesPerSecond/1024)} KB a second per player). Send less.`)}tell(e){let t=this.now();t-this.warnedAt<Wn||(this.warnedAt=t,this.warn(e))}};function q(n){try{let e=JSON.stringify(n)??"null",t=0;for(let r=0;r<e.length;r++)e.charCodeAt(r)>127&&(t+=2);return e.length+t+32}catch{return 1024}}var Ce=Math.PI*2;function Le(n,e,t,r={},s=""){if(typeof n=="number"&&typeof e=="number"){if(r.angles?.includes(s)){let o=((e-n)%Ce+Ce+Math.PI)%Ce-Math.PI;return n+o*t}return n+(e-n)*t}if(Array.isArray(n)&&Array.isArray(e)&&n.length===e.length)return e.map((o,i)=>Le(n[i],o,t,r,s));if(n&&e&&typeof n=="object"&&typeof e=="object"&&!Array.isArray(n)&&!Array.isArray(e)){let o={};for(let i of Object.keys(e))o[i]=i in n?Le(n[i],e[i],t,r,i):e[i];return o}return t<.5?n:e}function Jn(n,e,t){if(t===void 0||!n||!e||typeof n!="object"||typeof e!="object")return!1;for(let[r,s]of Object.entries(e)){let o=n[r];if(typeof s=="number"&&typeof o=="number"&&Math.abs(s-o)>t)return!0}return!1}function At(n,e,t={}){if(n.length===0)return;let r=n[0],s=n[n.length-1];if(e<=r.at)return r.d;if(e>=s.at)return s.d;let o=n.length-2;for(;o>0&&n[o].at>e;)o--;let i=n[o],l=n[o+1];return Jn(i.d,l.d,t.snap)?l.d:Le(i.d,l.d,(e-i.at)/Math.max(1,l.at-i.at),t)}var $t=8,Ct=2e3;var Yn=3e3,Qn=15e3,Zn=5e3;function Lt(n){let e={};for(let t of Object.keys(n))J(t)&&(e[t]=n[t]);return e}var I=()=>48,Z=class{constructor(e,t,r=Date.now,s=[]){p(this,"id");p(this,"kind");p(this,"invite");p(this,"teams");p(this,"open");p(this,"ranked");p(this,"kept");p(this,"me");p(this,"owner");p(this,"players",new Map);p(this,"state",{});p(this,"host");p(this,"closed",!1);p(this,"connected",!0);p(this,"match");p(this,"listeners",new Map);p(this,"transport");p(this,"outbox",new ce);p(this,"samples",new Map);p(this,"presenceTimer",null);p(this,"pendingPresence",null);p(this,"writes",0);p(this,"written",new Map);p(this,"confirmed",{});p(this,"shares",{messagesPerSecond:S.messagesPerSecond,presenceHz:S.presenceHz,bytesPerSecond:S.roomBytesPerSecond});p(this,"clock");p(this,"schema");p(this,"frozenSettings",null);p(this,"secrets",new Map);p(this,"offlineReady");p(this,"complained",new Map);p(this,"acks",new Map);this.transport=t,this.clock=r,this.schema=s,this.match=e.match??{phase:"lobby",n:0,min:1},this.applyBudget(e.budget),this.id=e.room,this.kind=e.room==="solo"?"solo":e.room.startsWith("private:")?"private":e.room.startsWith("room:")?"named":"public",this.invite=e.invite??null,this.teams=e.teams??0,this.open=e.open??!0,this.ranked=e.ranked??!1,this.kept=e.kept??Qe(e.room),this.owner=e.owner??null,this.host=e.host;for(let o of e.players)this.players.set(o.id,{...o}),o.presence!==null&&o.presence!==void 0&&this.samples.set(o.id,[{at:e.now??this.clock(),d:o.presence}]);this.me=this.players.get(e.you)??{id:e.you,name:"You",presence:null,team:0},this.players.set(this.me.id,this.me),this.state={...e.state},this.confirmed=Lt(e.state);for(let[o,i]of Object.entries(e.private??{}))this.secrets.set(o,{...i});this.match.phase!=="lobby"&&(this.frozenSettings=this.chosenSettings())}get budget(){return this.shares}get settings(){return this.frozenSettings??this.chosenSettings()}setSetting(e,t){if(this.closed||!this.isHost||this.match.phase!=="lobby")return;let r=this.schema.find(o=>o.id===e);if(!r||!r.options.some(o=>o.value===t)||this.chosenSettings()[e]===t)return;let s=this.state[Y];this.setState(Y,{...s!==null&&typeof s=="object"?s:{},[e]:t}),this.clearReady(),this.emit("settings",this.settings)}chosenSettings(){let e=this.state[Y];return Object.fromEntries(this.schema.map(t=>[t.id,et(t,e)]))}get isHost(){return this.connected&&this.host===this.me.id}get online(){return[...this.players.values()].filter(e=>e.connected!==!1)}get participants(){let e=this.match.participants;return this.match.phase==="lobby"||!e?[]:e.map(t=>this.players.get(t)).filter(t=>!!t)}get spectators(){let e=this.match.participants;return this.match.phase==="lobby"||!e?[]:this.online.filter(t=>!e.includes(t.id))}get leftOut(){let e=this.match.left;return this.match.phase==="lobby"||!e?.length?[]:this.spectators.filter(t=>e.includes(t.id))}get private(){return this.secrets.get(this.me.id)??{}}privateOf(e){return e===this.me.id||this.isHost?this.secrets.get(e)??{}:{}}setPrivate(e,t){this.closed||(this.setSecret(this.me.id,e,t??null),this.outbox.event(()=>q(t),()=>this.transport.private(e,t??null)))}setPrivateFor(e,t,r){if(!this.closed){if(e===this.me.id)return this.setPrivate(t,r);this.outbox.event(()=>q(r),()=>this.transport.private(t,r??null,String(e)))}}presenceAt(e,t={}){if(e===this.me.id)return this.me.presence;let r=this.samples.get(e);return!r||r.length===0?this.players.get(e)?.presence??null:At(r,this.clock()-(t.delay??100),t)}keep(e,t,r){let s=this.samples.get(e)??[],o=s.length;for(;o>0&&s[o-1].at>t;)o--;for(s.splice(o,0,{at:t,d:r});s.length>$t||s.length>2&&s[0].at<t-Ct;)s.shift();this.samples.set(e,s)}isParticipant(e=this.me.id){return this.match.phase!=="lobby"&&!!this.match.participants?.includes(e)}get spectating(){return this.match.phase!=="lobby"&&!this.isParticipant()}get running(){return this.match.phase==="playing"&&!this.match.paused}matchNow(){let e=this.match;if(e.phase!=="playing"||e.startedAt===void 0)return 0;let t=this.clock(),r=(e.pausedMs??0)+(e.paused?Math.max(0,t-e.paused.since):0);return Math.max(0,t-e.startedAt-r)}get canStart(){return this.isHost&&this.kind!=="public"&&this.match.phase==="lobby"&&this.online.length>=this.match.min&&this.allReady}get allReady(){return this.notReady.length===0}get notReady(){return this.online.filter(e=>e.id!==this.host&&!e.ready)}setReady(e=!0){this.closed||!!this.me.ready===e||(e?this.me.ready=!0:delete this.me.ready,this.connected||(this.offlineReady=e),this.outbox.urgent(I,()=>this.transport.ready(e)))}startMatch(e={}){if(!this.reachable("startMatch"))return;let t=Number(e.countdown),r={...Number.isFinite(t)?{countdown:Math.round(t*1e3)}:{},...e.open===!0?{open:!0}:{}};this.outbox.urgent(I,()=>this.transport.start(r))}endMatch(){this.reachable("endMatch")&&this.outbox.urgent(I,()=>this.transport.end())}pauseMatch(e=!0){this.reachable("pauseMatch")&&this.outbox.urgent(I,()=>this.transport.pause(e))}admit(e){let t=(Array.isArray(e)?e:[e]).map(String);t.length&&this.reachable("admit")&&this.outbox.urgent(I,()=>this.transport.admit(t))}reachable(e){let[t,r]=this.closed?["closed","this room is closed (you left it, were removed, or moved to another one)"]:this.connected?["",""]:["disconnected","the connection to the room is down and the platform is reconnecting: call it again after room.on('reconnect')"];return t?(this.complain(e,t,r),!1):!0}dropHostWrites(){for(let e of[...this.written.keys()]){if(!J(e))continue;this.written.delete(e);let t=this.confirmed[e]??null;JSON.stringify(this.state[e]??null)!==JSON.stringify(t)&&(t===null?delete this.state[e]:this.state[e]=t,this.emit("state",e,t,""))}}complain(e,t,r){let s=this.clock(),o=`${e}:${t}`;if(s-(this.complained.get(o)??-1/0)<Zn)return;this.complained.set(o,s);let i=`${e}() did nothing: ${r}.`;console.warn(`[onceworlds] ${i} (${t})`),this.emit("error",{code:t,message:i})}transferHost(e){this.closed||this.outbox.urgent(I,()=>this.transport.transfer(String(e)))}hideLobby(e=!0){this.closed||this.transport.hideLobby?.(!!e)}clearReady(){if(!(this.closed||!this.isHost)){for(let e of this.players.values())delete e.ready;this.outbox.urgent(I,()=>this.transport.unready())}}send(e,t={}){this.closed||this.outbox.event(()=>q(e),()=>this.transport.send(e,t.to))}setPresence(e){if(!this.closed){if(this.me.presence=e,this.presenceTimer){this.pendingPresence={value:e};return}this.transport.presence(e),this.outbox.spend(q(e)),this.presenceTimer=setTimeout(()=>this.flushPresence(),1e3/this.shares.presenceHz)}}setState(e,t){this.writeState(e,t)}writeState(e,t){if(this.closed)return 0;if(J(e)&&this.connected&&this.host!==this.me.id)return this.complain("setState","not_host",`only the host writes ${e}`),0;t==null?delete this.state[e]:this.state[e]=t;let r=++this.writes;return this.written.set(e,{w:r,at:this.clock()}),this.outbox.state(e,()=>q(t)+e.length,()=>this.transport.state(e,t??null,r)),r}_writeAcked(e,t){if(this.closed||!this.connected)return Promise.resolve(!1);let r=this.writeState(e,t);return r?this.kind==="solo"?Promise.resolve(!0):new Promise(s=>{let o={w:r,done:l=>{clearTimeout(i),s(l)}},i=setTimeout(()=>this.settleAcks(e,l=>l===o,!1),Qn);this.acks.set(e,[...this.acks.get(e)??[],o])}):Promise.resolve(!1)}_resetData(){return this.closed||!this.connected||!this.transport.dataReset?!1:(this.outbox.urgent(I,()=>this.transport.dataReset()),!0)}settleAcks(e,t,r){let s=this.acks.get(e);if(!s)return;let o=s.filter(i=>!t(i));o.length>0?this.acks.set(e,o):this.acks.delete(e);for(let i of s)t(i)&&i.done(r)}failAcks(){for(let e of[...this.acks.keys()])this.settleAcks(e,()=>!0,!1)}setTeam(e,t){this.closed||this.outbox.urgent(I,()=>this.transport.team(e,t))}setOpen(e){this.closed||this.outbox.urgent(I,()=>this.transport.open(e))}reportResult(e){this.closed||this.outbox.urgent(()=>q(e),()=>this.transport.result(e))}kick(e){this.closed||this.outbox.urgent(I,()=>this.transport.kick(String(e)))}voteKick(e){this.closed||this.outbox.urgent(I,()=>this.transport.voteKick(String(e)))}on(e,t){let r=this.listeners.get(e);return r||this.listeners.set(e,r=new Set),r.add(t),()=>r.delete(t)}leave(){this.closed||(this.transport.leave(),this.close("left"))}_receive(e){switch(e.t){case"welcome":return this.resync(e);case"away":{let t=this.players.get(e.id);return!t||t===this.me?void 0:(t.connected=!1,this.emit("away",t))}case"back":{let t=this.players.get(e.id);return!t||t===this.me?void 0:(delete t.connected,this.emit("back",t,e.fresh===!0))}case"ready":{let t=this.players.get(e.id);return!t||!!t.ready==(e.ready===!0)?void 0:(e.ready===!0?t.ready=!0:delete t.ready,this.emit("ready",t))}case"match":return this.applyMatch(e.match);case"idle":{let t=this.players.get(e.id);return!t||t===this.me||!!t.idle==(e.idle===!0)?void 0:(e.idle===!0?t.idle=!0:delete t.idle,this.emit("idle",t))}case"private":{let t=JSON.stringify(this.secrets.get(e.id)?.[e.k]??null);this.setSecret(e.id,e.k,e.v??null),t!==JSON.stringify(e.v??null)&&this.emit("private",e.k,e.v??null,e.id);return}case"privates":return this.applySecrets(e.all??{});case"unready":{for(let t of this.players.values())t.ready&&(delete t.ready,this.emit("ready",t));return}case"join":{let t={...e.player};return this.players.set(t.id,t),this.emit("join",t)}case"leave":{let t=this.players.get(e.id);return!t||t===this.me?void 0:(this.players.delete(e.id),this.samples.delete(e.id),this.emit("leave",t,e.kicked===!0))}case"votekick":{let t=this.players.get(e.id);t&&this.emit("votekick",t,e.votes,e.needed);return}case"host":if(this.host=e.id,e.id!==this.me.id)for(let t of[...this.secrets.keys()])t!==this.me.id&&this.secrets.delete(t);return e.id!==this.me.id&&(this.dropHostWrites(),this.failAcks()),this.emit("host",e.id);case"msg":return this.emit("message",e.d,this.players.get(e.from)??{id:e.from,name:"Unknown",presence:null,team:0},typeof e.at=="number"?e.at:this.clock(),typeof e.mt=="number"?e.mt:void 0);case"team":{let t=this.players.get(e.id);return t?(t.team=e.team,this.emit("team",t)):void 0}case"name":{let t=e.id===this.me.id?this.me:this.players.get(e.id);return!t||typeof e.name!="string"||t.name===e.name?void 0:(t.name=e.name,this.emit("rename",t))}case"open":return this.open=e.open,this.emit("open",e.open);case"rated":for(let t of e.changes){let r=this.players.get(t.id);r&&(r.rating=t.rating)}return this.emit("rated",e.changes,e.disputed,e.capped===!0);case"presence":{let t=this.players.get(e.id);return!t||t===this.me?void 0:(t.presence=e.d,this.keep(e.id,typeof e.at=="number"?e.at:this.clock(),e.d),this.emit("presence",t))}case"state":{e.from===this.me.id&&typeof e.w=="number"&&this.settleAcks(e.k,s=>s.w<=e.w,!0),J(e.k)&&(e.v===null?delete this.confirmed[e.k]:this.confirmed[e.k]=e.v);let t=this.written.get(e.k);if(t)if(e.from===this.me.id)if(typeof e.w!="number")this.written.delete(e.k);else{if(e.w<t.w)return;e.w===t.w&&this.written.delete(e.k)}else{if(this.clock()-t.at<Yn)return;this.written.delete(e.k)}let r=JSON.stringify(this.state[e.k]??null);e.v===null?delete this.state[e.k]:this.state[e.k]=e.v,(e.from!==this.me.id||r!==JSON.stringify(e.v??null))&&this.emit("state",e.k,e.v,e.from),e.k===Y&&this.schema.length>0&&this.frozenSettings===null&&r!==JSON.stringify(e.v??null)&&e.from!==this.me.id&&this.emit("settings",this.settings);return}case"chat":return this.emit("chat",e.entry);case"error":return typeof e.k=="string"&&typeof e.w=="number"&&this.settleAcks(e.k,t=>t.w===e.w,!1),e.code==="cooldown"&&this.me.ready&&(delete this.me.ready,this.emit("ready",this.me)),console.warn(`[onceworlds] ${e.message} (${e.code})`),this.emit("error",{code:e.code,message:e.message})}}_closed(e){this.close(e)}_disconnected(){this.closed||!this.connected||(this.connected=!1,this.outbox.clear(),this.failAcks(),this.emit("disconnect"))}close(e){this.closed||(this.closed=!0,this.outbox.clear(),this.failAcks(),this.presenceTimer&&clearTimeout(this.presenceTimer),this.emit("close",e))}flushPresence(){if(this.presenceTimer=null,!this.pendingPresence||this.closed)return;let{value:e}=this.pendingPresence;this.pendingPresence=null,this.transport.presence(e),this.outbox.spend(q(e)),this.presenceTimer=setTimeout(()=>this.flushPresence(),1e3/this.shares.presenceHz)}applyBudget(e){if(!e)return;let t=(r,s)=>typeof r=="number"&&Number.isFinite(r)&&r>0&&r<=s;t(e.presenceHz,S.presenceHz)&&(this.shares={...this.shares,presenceHz:e.presenceHz}),t(e.messagesPerSecond,S.messagesPerSecond)&&t(e.bytesPerSecond,S.roomBytesPerSecond)&&(this.shares={...this.shares,messagesPerSecond:e.messagesPerSecond,bytesPerSecond:e.bytesPerSecond},this.outbox.setBudget({messagesPerSecond:e.messagesPerSecond,bytesPerSecond:e.bytesPerSecond}))}setSecret(e,t,r){let s={...this.secrets.get(e)??{}};r===null?delete s[t]:s[t]=r,Object.keys(s).length>0?this.secrets.set(e,s):this.secrets.delete(e)}applySecrets(e){let t=[];for(let[r,s]of Object.entries(e))for(let[o,i]of Object.entries(s))JSON.stringify(this.secrets.get(r)?.[o]??null)!==JSON.stringify(i)&&t.push([r,o,i]);for(let[r,s]of this.secrets)for(let o of Object.keys(s))o in(e[r]??{})||t.push([r,o,null]);this.secrets=new Map(Object.entries(e).map(([r,s])=>[r,{...s}]));for(let[r,s,o]of t)this.emit("private",s,o,r)}applyMatch(e){let t=this.match;if(!(!e||JSON.stringify(t)===JSON.stringify(e))){if(this.match=e,e.phase!=="lobby"&&this.frozenSettings===null)this.frozenSettings=this.chosenSettings();else if(e.phase==="lobby"&&this.frozenSettings!==null){let r=JSON.stringify(this.frozenSettings);this.frozenSettings=null,r!==JSON.stringify(this.chosenSettings())&&queueMicrotask(()=>this.emit("settings",this.settings))}this.emit("match",e,t),t.phase!=="starting"&&e.phase==="starting"&&this.emit("starting",e),t.phase!=="playing"&&e.phase==="playing"&&this.emit("matchstart",e),t.phase!=="lobby"&&e.phase==="lobby"&&this.emit("matchend",e,t),t.phase==="playing"&&e.phase==="playing"&&(!t.paused&&e.paused?this.emit("matchpause",e):t.paused&&!e.paused&&this.emit("matchresume",e))}}resync(e){let t=this.connected,r=this.host!==e.host;this.connected=!0,this.host=e.host,this.written.clear(),this.confirmed=Lt(e.state),this.applyBudget(e.budget);let s=new Set;for(let i of e.players){if(s.add(i.id),i.id===this.me.id){this.offlineReady===void 0&&!!this.me.ready!=!!i.ready&&(i.ready?this.me.ready=!0:delete this.me.ready,this.emit("ready",this.me));continue}let l=this.players.get(i.id);if(l)l.presence=i.presence,i.presence!==null&&i.presence!==void 0&&this.keep(i.id,e.now??this.clock(),i.presence),l.team=i.team,l.name!==i.name&&(l.name=i.name,this.emit("rename",l)),!!l.ready!=!!i.ready&&(i.ready?l.ready=!0:delete l.ready,this.emit("ready",l)),!!l.idle!=!!i.idle&&(i.idle?l.idle=!0:delete l.idle,this.emit("idle",l)),l.connected===!1!=(i.connected===!1)&&(i.connected===!1?l.connected=!1:delete l.connected,i.connected===!1?this.emit("away",l):this.emit("back",l,!1));else{let c={...i};this.players.set(i.id,c),this.emit("join",c)}}for(let[i,l]of this.players)s.has(i)||l===this.me||(this.players.delete(i),this.emit("leave",l));let o=new Set([...Object.keys(this.state),...Object.keys(e.state)]);for(let i of o){let l=e.state[i]??null;JSON.stringify(this.state[i]??null)!==JSON.stringify(l)&&(l===null?delete this.state[i]:this.state[i]=l,this.emit("state",i,l,""))}r&&this.emit("host",e.host),this.teams=e.teams??this.teams,this.kept=e.kept??this.kept,e.open!==void 0&&e.open!==this.open&&(this.open=e.open,this.emit("open",e.open)),this.applyMatch(e.match??{phase:"lobby",n:0,min:1}),this.applySecrets(e.private??{}),this.me.presence!==null&&this.transport.presence(this.me.presence),this.offlineReady!==void 0&&!!e.players.find(i=>i.id===this.me.id)?.ready!==this.offlineReady&&this.transport.ready(this.offlineReady),this.offlineReady=void 0,t||this.emit("reconnect")}emit(e,...t){for(let r of this.listeners.get(e)??[])try{r(...t)}catch(s){console.error(`[onceworlds] error in room "${e}" listener`,s)}}};function _t(n){let e=null,t=s=>n()._receive({t:"match",match:s}),r=()=>{let o=n().match;if(o.phase!=="starting")return;let{startsAt:i,...l}=o;t({...l,phase:"playing",startedAt:Date.now(),pausedMs:0})};return{send(){},presence(){},state(){},team(){},open(){},result(){},kick(){},voteKick(){},ready(){},unready(){},transfer(){},start(s){let o=n();if(o.match.phase!=="lobby")return;let i=Math.min(S.matchCountdownMaxMs,Math.max(0,s.countdown??S.matchCountdownMs));t({phase:"starting",n:o.match.n+1,min:o.match.min,id:`solo${o.match.n+1}`,seed:Math.floor(Math.random()*2**32),participants:[o.me.id],startsAt:Date.now()+i}),i===0?r():e=setTimeout(r,i)},end(){let s=n();e&&clearTimeout(e),e=null,s.match.phase!=="lobby"&&t({phase:"lobby",n:s.match.n,min:s.match.min})},pause(s){let o=n().match;if(o.phase!=="playing")return;let i=Date.now();if(s&&!o.paused)t({...o,paused:{since:i,reason:"host"}});else if(!s&&o.paused){let{paused:l,...c}=o;t({...c,pausedMs:(o.pausedMs??0)+Math.max(0,i-l.since)})}},admit(){},private(){},leave(){e&&clearTimeout(e),e=null}}}var Dt=["pointerdown","pointermove","keydown","wheel","touchstart","touchmove","devicemotion","visibilitychange"],Xn=5e3;function Ot(n,e=S.idleAfterMs){let t=Date.now(),r=!1,s=()=>{t=Date.now(),r&&(r=!1,n(!1))},o=()=>{try{for(let l of navigator.getGamepads?.()??[])if(l&&(l.buttons.some(c=>c.pressed)||l.axes.some(c=>Math.abs(c)>.25)))return!0}catch{}return!1};for(let l of Dt)window.addEventListener(l,s,{capture:!0,passive:!0});let i=setInterval(()=>{if(o())return s();!r&&Date.now()-t>e&&(r=!0,n(!0))},Xn);return{get idle(){return r},stop(){clearInterval(i);for(let l of Dt)window.removeEventListener(l,s,{capture:!0})}}}var Tt=["pointerdown","keydown","touchstart","gamepadconnected"];function It(n,e){let t=r=>{if(!(!r.isTrusted||!n())){for(let s of Tt)window.removeEventListener(s,t,{capture:!0});e()}};for(let r of Tt)window.addEventListener(r,t,{capture:!0,passive:!0})}var Nt=["low","medium","high"],er={low:.6,medium:.8,high:1},tr=24,nr=18.5,rr=26,_e=6e3,Bt=2e4,or=5*6e4,De=class{constructor(e=2){p(this,"level",2);p(this,"slow",0);p(this,"smoothSince",0);p(this,"holdUntil",0);p(this,"probeAfter",Bt);p(this,"steppedUpAt",-1/0);this.level=e}window(e,t,r){return t>tr?(this.smoothSince=0,++this.slow>=2&&e>=this.holdUntil&&this.level>0&&(e-this.steppedUpAt<_e+Bt/2&&(this.probeAfter=Math.min(or,this.probeAfter*2)),this.level--,this.slow=0,this.holdUntil=e+_e)):t<=nr&&r<=rr?(this.slow=0,this.smoothSince||(this.smoothSince=e),this.level<Nt.length-1&&e>=this.holdUntil&&e-this.smoothSince>=this.probeAfter&&(this.level++,this.steppedUpAt=e,this.smoothSince=0,this.holdUntil=e+_e)):(this.slow=0,this.smoothSince=0),this.level}};function Gt(n){let e="auto",t=typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches,r=!1,s=new De(sr()?1:2),o=new Set,i=!1,l=!1,c=0,d=[],m=0,h=0,f=M=>{if(i){if(c&&!document.hidden){let P=M-c;P<250&&d.push(P)}if(c=document.hidden?0:M,m||(m=M),M-m>=1e3){if(d.length>=10){let P=[...d].sort((j,A)=>j-A),L=d.reduce((j,A)=>j+A,0)/d.length,U=P[Math.min(P.length-1,Math.floor(P.length*.95))],N=E();e==="auto"&&s.window(M,L,U),r&&M-h>=500&&(h=M,n.stats({fps:Math.round(1e3/L),frameMs:Math.round(L*10)/10,worstMs:Math.round(P[P.length-1]*10)/10})),E()!==N&&_()}d=[],m=M}w()}},w=()=>{l||!i||(l=!0,requestAnimationFrame(M=>{l=!1,f(M)}))},y=()=>{i||typeof requestAnimationFrame!="function"||(i=!0,c=0,m=0,w())},k=()=>{!r&&e!=="auto"&&i&&(i=!1)},E=()=>e==="auto"?Nt[s.level]:e,_=()=>{for(let M of o)try{M(T)}catch(P){console.error('[onceworlds] error in settings "change" listener',P)}},T={get choice(){return e},get quality(){return e==="auto"&&y(),E()},get scale(){return er[T.quality]},get reducedMotion(){return t},pixelRatio(M=2){let P=typeof devicePixelRatio=="number"&&devicePixelRatio>0?devicePixelRatio:1;return Math.max(.5,Math.min(P,M)*T.scale)},on(M,P){return M!=="change"?()=>{}:(o.add(P),e==="auto"&&y(),()=>o.delete(P))}};return{settings:T,apply(M){let P=`${E()}|${t}`;e=M.quality,t=M.reducedMotion,r=M.showStats,r||e==="auto"&&o.size>0?y():k(),`${E()}|${t}`!==P&&_()}}}function sr(){return typeof matchMedia=="function"&&matchMedia("(pointer: coarse)").matches}var X={maxSide:1280,quality:.85,maxBytes:15e5,answerMs:2500,waitMs:3e3,perSecond:3};var ir=4,ar=250,lr=1e3;function cr(){return typeof window>"u"||typeof document>"u"?null:{document,requestAnimationFrame:n=>window.requestAnimationFrame(n),setTimeout:(n,e)=>window.setTimeout(n,e),clearTimeout:n=>window.clearTimeout(n),background:n=>window.getComputedStyle(n).backgroundColor,hidden:()=>document.visibilityState==="hidden"}}function ur(n){if(!n||typeof n!="object")return null;let e=n;if(typeof e.id!="number"||!Number.isSafeInteger(e.id)||e.id<0)return null;let t=typeof e.maxSide=="number"&&Number.isFinite(e.maxSide)?Math.min(X.maxSide,Math.max(64,Math.round(e.maxSide))):X.maxSide,r=typeof e.quality=="number"&&Number.isFinite(e.quality)?Math.min(.95,Math.max(.5,e.quality)):X.quality;return{id:e.id,maxSide:t,quality:r}}function dr(n,e,t){let r=Math.min(1,t/Math.max(n,e,1)),s=o=>Math.max(1,Math.min(t,Math.round(o*r)));return{width:s(n),height:s(e)}}function mr(n){for(let e=0;e+2<n.length;e+=4)if(n[e]!==0||n[e+1]!==0||n[e+2]!==0)return!1;return!0}var pr=/^(?:transparent|rgba\(.*,\s*0(?:\.0*)?\))$/;function fr(n,e){try{for(let t=n;t;t=t.parentElement){let r=e.background?.(t)?.trim();if(r&&!pr.test(r))return r}}catch{}return"#000"}function Oe(n,e,t,r,s){try{let o=r.document.createElement("canvas");o.width=e.width,o.height=e.height;let i=o.getContext("2d");if(!i)return{miss:"unsupported"};if(i.drawImage(n,0,0,e.width,e.height),mr(i.getImageData(0,0,e.width,e.height).data))return{miss:"blank"};i.globalCompositeOperation="destination-over",i.fillStyle=s(),i.fillRect(0,0,e.width,e.height);for(let l of[t.quality,.6]){let c=o.toDataURL("image/jpeg",l);if(!c.startsWith("data:image/jpeg;base64,"))return{miss:"unsupported"};if((c.length-23)*.75<=X.maxBytes)return{image:c}}return{miss:"failed"}}catch(o){return{miss:o?.name==="SecurityError"?"tainted":"failed"}}}function jt(n,e){let t=!1,r=()=>{t||(t=!0,e())};n.requestAnimationFrame(r),n.setTimeout(r,ar)}function gr(n,e,t){return new Promise(r=>{let s,o;try{if(typeof n.captureStream!="function")return r(null);s=n.captureStream(),o=e.document.createElement("video"),o.muted=!0,o.playsInline=!0,o.srcObject=s}catch{return r(null)}let i=!1,l=m=>{if(!i){i=!0,e.clearTimeout(c);try{o.srcObject=null;for(let h of s.getTracks())h.stop()}catch{}r(m)}},c=e.setTimeout(()=>l(null),lr),d=()=>{!i&&o.videoWidth>0&&o.videoHeight>0&&l(t(o))};try{o.addEventListener("loadeddata",d),o.addEventListener("playing",d),o.play()?.catch(()=>{})}catch{l(null)}})}function Ht(n,e=cr()){let t=!1,r=o=>{try{n(o)}catch{}};function s(o){let i=o.id;if(!e)return r({id:i,t:"none",reason:"unsupported"});if(t)return r({id:i,t:"none",reason:"busy"});let l=e,c=ie(l.document.querySelectorAll("canvas"));if(!c)return r({id:i,t:"none",reason:"no-canvas"});t=!0;let d=k=>{t=!1,r("image"in k?{id:i,t:"done",image:k.image}:{id:i,t:"none",reason:k.miss})},m=dr(c.width,c.height,o.maxSide),h=null,f=()=>h??(h=fr(c,l));if(l.hidden?.()){let k=Oe(c,m,o,l,f);return d("miss"in k&&k.miss==="blank"?{miss:"hidden"}:k)}let w=0,y=()=>{try{let k=Oe(c,m,o,l,f);if(!("miss"in k)||k.miss!=="blank")return d(k);if(++w<ir)return jt(l,y);gr(c,l,E=>Oe(E,m,o,l,f)).then(E=>d(E??{miss:"blank"}),()=>d({miss:"blank"}))}catch{d({miss:"failed"})}};jt(l,y)}return{receive(o){let i=null;try{i=ur(o),i&&s(i)}catch{t=!1,i&&r({id:i.id,t:"none",reason:"failed"})}}}}var qt=(n,e)=>Object.prototype.hasOwnProperty.call(n,e);function Te(n){try{return n(),!0}catch{return!1}}function hr(){let n=new Map,e={getItem:t=>n.has(String(t))?n.get(String(t)):null,setItem:(t,r)=>{n.set(String(t),String(r))},removeItem:t=>{n.delete(String(t))},clear:()=>n.clear(),key:t=>[...n.keys()][Number(t)]??null};return new Proxy(e,{get(t,r){if(r==="length")return n.size;if(r===Symbol.toStringTag)return"Storage";if(typeof r=="string")return qt(e,r)?t[r]:n.get(r)},set(t,r,s){return typeof r=="string"&&n.set(r,String(s)),!0},has:(t,r)=>typeof r=="string"&&(qt(e,r)||n.has(r)),deleteProperty(t,r){return typeof r=="string"&&n.delete(r),!0},ownKeys:()=>[...n.keys()],getOwnPropertyDescriptor:(t,r)=>typeof r=="string"&&n.has(r)?{value:n.get(r),writable:!0,enumerable:!0,configurable:!0}:void 0})}function yr(n=Date.now){let e=new Map;return{read:()=>[...e].map(([t,r])=>`${t}=${r}`).join("; "),write(t){let[r,...s]=String(t).split(";"),o=r.indexOf("="),i=(o<0?"":r.slice(0,o)).trim(),l=(o<0?r:r.slice(o+1)).trim();if(!i&&!l)return;let c=!1;for(let d of s){let[m,...h]=d.split("="),f=h.join("=").trim();m.trim().toLowerCase()==="max-age"&&Number(f)<=0&&(c=!0),m.trim().toLowerCase()==="expires"&&Date.parse(f)<=n()&&(c=!0)}c?e.delete(i):e.set(i,l)}}}function ee(n,e,t){try{Object.defineProperty(n,e,{configurable:!0,...t})}catch{}}function br(){if(typeof HTMLImageElement!="function")return;let n=Object.getOwnPropertyDescriptor(HTMLImageElement.prototype,"src");if(!n?.get||!n.set)return;let{get:e,set:t}=n;ee(HTMLImageElement.prototype,"src",{enumerable:n.enumerable,get(){return e.call(this)},set(r){try{this.crossOrigin===null&&new URL(String(r),document.baseURI).origin===location.origin&&(this.crossOrigin="anonymous")}catch{}t.call(this,r)}})}function wr(){let n=window.Worker;if(typeof n!="function")return;let e=function(t,r){try{return new n(t,r)}catch(s){if(!(s instanceof DOMException&&s.name==="SecurityError")||r?.type==="module")throw s;let o;try{o=new URL(String(t),document.baseURI)}catch{throw s}if(o.origin!==location.origin)throw s;let l=`const base = ${JSON.stringify(o.href)};
|
|
3
|
+
const resolve = (input) => new URL(String(input), base).href;
|
|
4
|
+
const importScripts_ = self.importScripts.bind(self);
|
|
5
|
+
self.importScripts = (...urls) => importScripts_(...urls.map(resolve));
|
|
6
|
+
const fetch_ = self.fetch.bind(self);
|
|
7
|
+
self.fetch = (input, init) => fetch_(typeof input === 'string' || input instanceof URL ? resolve(input) : input, init);
|
|
8
|
+
if (typeof XMLHttpRequest === 'function') {
|
|
9
|
+
const open_ = XMLHttpRequest.prototype.open;
|
|
10
|
+
XMLHttpRequest.prototype.open = function (method, url, ...rest) { return open_.call(this, method, resolve(url), ...rest); };
|
|
11
|
+
}
|
|
12
|
+
importScripts(base);`;return new n(URL.createObjectURL(new Blob([l],{type:"text/javascript"})),r)}};e.prototype=n.prototype,ee(window,"Worker",{value:e,writable:!0})}function zt(){try{for(let n of["localStorage","sessionStorage"])Te(()=>window[n].length)||ee(window,n,{value:hr()});if(!Te(()=>document.cookie)){let n=yr();ee(document,"cookie",{get:()=>n.read(),set:e=>n.write(e)})}if(window.origin==="null"&&(wr(),br()),window.origin==="null"||!Te(()=>history.replaceState(history.state,"",location.href)))for(let n of["pushState","replaceState"]){let e=history[n];ee(history,n,{value:function(...t){try{return e.apply(this,t)}catch(r){if(!(r instanceof DOMException&&r.name==="SecurityError"))throw r}}})}}catch{}}var kr=["menu","lobby","stick","buttons"];function Ft(n){if(!Array.isArray(n)||n.length>16)return null;let e=[];for(let t of n){if(!t||typeof t!="object")continue;let{kind:r,x:s,y:o,w:i,h:l}=t;kr.includes(r)&&(!ue(s)||!ue(o)||!ue(i)||!ue(l)||i<0||l<0||e.push({kind:r,x:s,y:o,w:i,h:l}))}return e}var ue=n=>typeof n=="number"&&Number.isFinite(n),Ie={entries:10,entryMs:1e3,totalMs:2e3};function Kt(n){let e=typeof n=="number"?[n]:Array.isArray(n)?n.slice(0,Ie.entries):null;if(!e||!e.every(s=>typeof s=="number"&&Number.isFinite(s)&&s>=0))return null;let t=[],r=Ie.totalMs;for(let s of e){let o=Math.min(Math.round(s),Ie.entryMs,r);if(t.push(o),r-=o,r<=0)break}return t.some((s,o)=>o%2===0&&s>0)?t:[]}var vr=/^#(?:[0-9a-f]{3}){1,2}$/i,te={panel:"#111216",text:"#f7f7f8",accent:"#d33e00"},Ne=4.5,Be=3,de=["#4ade80","#15803d"];function Vt(n){let e=[];if(n==null)return{look:null,notes:e};if(typeof n!="object")return{look:null,notes:["a look is { accent, panel, text } or null"]};let t=n,r=m=>{let h=t[m];return h==null?null:typeof h=="string"&&vr.test(h)?Wt(h):(e.push(`${m} is not #rgb or #rrggbb`),null)},s=r("panel"),o=r("text"),i=r("accent");if(s||o){let m=z(o??te.text,s??te.panel);m<Ne&&(e.push(`text on panel is ${m.toFixed(1)}:1, under ${Ne}:1: the platform's are used`),s=o=null)}let l=s??te.panel,c=o??te.text;if(i){let m=z(i,l);m<Be&&(e.push(`accent on panel is ${m.toFixed(1)}:1, under ${Be}:1: the platform's is used`),i=null)}if(!i&&s&&z(te.accent,s)<Be&&(i=c),!s&&!o&&!i)return{look:null,notes:e};let d=i?Sr(i):null;return{look:{panel:s,text:o,muted:s||o?xr(c,l):null,accent:i,accentHover:i?Jt(i,d==="#ffffff"?"#000000":"#ffffff",.14):null,onAccent:d,ok:s?z(de[0],s)>=z(de[1],s)?de[0]:de[1]:null},notes:e}}function Wt(n){let e=n.slice(1).toLowerCase();return`#${e.length===3?[...e].map(t=>t+t).join(""):e}`}function Ge(n){let e=Wt(n);return[parseInt(e.slice(1,3),16),parseInt(e.slice(3,5),16),parseInt(e.slice(5,7),16)]}function Ut(n){let[e,t,r]=Ge(n).map(s=>{let o=s/255;return o<=.04045?o/12.92:((o+.055)/1.055)**2.4});return .2126*e+.7152*t+.0722*r}function z(n,e){let[t,r]=[Ut(n),Ut(e)].sort((s,o)=>o-s);return(t+.05)/(r+.05)}function Sr(n){return z(n,"#000000")>z(n,"#ffffff")?"#000000":"#ffffff"}function Jt(n,e,t){let[r,s]=[Ge(n),Ge(e)];return`#${r.map((o,i)=>Math.round(o+(s[i]-o)*t).toString(16).padStart(2,"0")).join("")}`}function xr(n,e){for(let t=.3;t>.001;t-=.05){let r=Jt(n,e,t);if(z(r,e)>=Ne)return r}return n}var me=class{constructor(){p(this,"areas",null);p(this,"key","")}receive(e){let t=Ft(e);if(!t)return!1;let r=JSON.stringify(t);return this.areas&&r===this.key?!1:(this.key=r,this.areas=Object.freeze(t.map(s=>Object.freeze(s))),!0)}};function Yt(n){let e={look:null,notes:["a look is { accent, panel, text } or null"]};if(n==null)return{look:null,notes:[]};if(typeof n!="object")return e;let t={};try{for(let r of["accent","panel","text"]){let s=n[r];s!=null&&(t[r]=typeof s=="string"?s.slice(0,16):"")}}catch{return e}return{look:t,notes:Vt(t).notes}}function Qt(n,e,t){let r=typeof n.vibrate=="function"?n.vibrate.bind(n):null,s=o=>{try{if(!e())return r?r(o):!1;if(!r)return!1;let i=Kt(Array.isArray(o)?Array.from(o):o);return i?(t(i),!0):!1}catch{return!1}};try{Object.defineProperty(n,"vibrate",{value:s,configurable:!0,writable:!0})}catch{}}var Mr=["RTCPeerConnection","webkitRTCPeerConnection","mozRTCPeerConnection"];function Zt(n){if(n)for(let e of Mr)try{Object.defineProperty(n,e,{value:void 0,writable:!1,enumerable:!1,configurable:!1})}catch{}}function Xt(n,e){if(Zt(n),!!e)for(let t of["contentWindow","contentDocument"])try{let r=Object.getOwnPropertyDescriptor(e.prototype,t),s=r?.get;if(!r||!s)continue;Object.defineProperty(e.prototype,t,{...r,get(){let o=s.call(this);return Zt(t==="contentWindow"?o:o?.defaultView),o}})}catch{}}var en="0.1.0";function on(){let n=globalThis;return n.onceworlds?.__onceworlds||(n.onceworlds=Rr()),n.onceworlds}function Rr(){let n=lt(document.currentScript);zt(),Xt(window,typeof HTMLIFrameElement>"u"?void 0:HTMLIFrameElement);let e=at(),t=Gt({stats:a=>x("stats",a)}),r=window.parent!==window,s=r&>(location.search)?wt(console,location.origin):null,o="connecting",i=!r&&bt(location.search),l=null,c=null,d=1,m=new Map,h=new Map,f=null,w=[],y,k=null,E=0,_=new Set,T=new me,M=new Set,P=new Set,L=r?mt(a=>x("clip",a),void 0,()=>f?{phase:f.match.phase,paused:!!f.match.paused,flow:ut(f.state)}:null):null,U=s?Ht(a=>x("snapshot",a)):null,N=new le(a=>{o!=="standalone"&&R("ui.controls",a).catch(()=>{})},a=>x("input",{touch:a})),j,A=new Promise(a=>j=a),je=xt(),He=Mt(()=>f,je),sn={read:a=>R("data.game.get",{key:a}),op:a=>R("data.game.op",a)},an=Et(()=>G().then(a=>a.player),B),ln=Rt(async()=>(await A,o==="platform"?sn:an),je),be=new Set,we=[],qe=a=>{let u=`${a.message}|${a.source}|${a.line}`;be.has(u)||be.size>=S.errorReportsPerPage||(be.add(u),o==="platform"?x("error",a):o==="connecting"&&we.push(a))};if(window.addEventListener("error",a=>{if(!(a instanceof ErrorEvent))return;let u=String(a.message||"Error");if(qe({message:u,source:tn(a.filename),line:a.lineno||0,column:a.colno||0,stack:a.error instanceof Error?String(a.error.stack??""):""}),s){let g=a.error instanceof Error?`Uncaught ${ae(a.error)}`:/^Uncaught\b/.test(u)?u:`Uncaught ${u}`;s.error(g,{source:tn(a.filename),line:a.lineno||0,column:a.colno||0})}}),window.addEventListener("unhandledrejection",a=>{let u=a.reason;s?.error(`Uncaught (in promise) ${u instanceof Error?ae(u):V(u)}`),qe({message:`Unhandled promise rejection: ${u instanceof Error?u.message:String(u)}`,source:"",line:0,column:0,stack:u instanceof Error?String(u.stack??""):""})}),r){window.addEventListener("message",u=>{if(!(u.source!==window.parent||u.data?.type!==nt||!u.ports[0])&&o!=="standalone"){if(c?.close(),c=u.ports[0],c.onmessage=g=>cn(g.data),o=ht(u.data),i=o==="platform"&&yt(u.data),j(),o==="platform")for(let g of we.splice(0))x("error",g);else we.length=0;s?.connect(g=>x("console",g)),y||(x("alive",{}),y=setInterval(()=>x("alive",{}),2e3)),o==="platform"&&G().then(g=>{e.apply(g.audio),g.settings&&t.apply(g.settings)}).catch(()=>{})}});let a={type:tt,version:en,...n?{build:n}:{}};window.parent.postMessage(a,"*"),setTimeout(()=>{o==="connecting"&&(o="standalone",j())},5e3)}else o="standalone",j();if(window.addEventListener("keydown",a=>{a.key!=="/"||o!=="platform"||!f||Ar(a.target)||(a.preventDefault(),a.stopImmediatePropagation(),x("hotkey",{key:"/"}))},!0),r){try{document.documentElement.style.touchAction="pan-x pan-y"}catch{}for(let a of["gesturestart","gesturechange","gestureend"])document.addEventListener(a,u=>u.preventDefault(),{passive:!1})}if(r){let a=u=>{u.pointerType==="mouse"||o!=="platform"||(window.removeEventListener("pointerup",a,!0),R("ui.tapped",void 0).catch(()=>{}))};window.addEventListener("pointerup",a,!0)}if(r&&It(()=>o==="platform",()=>x("input.first",{})),r&&Qt(navigator,()=>o==="platform",a=>x("vibrate",{pattern:a})),r){let a=Element.prototype.requestFullscreen;Element.prototype.requestFullscreen=function(u){return o!=="platform"?a.call(this,u):(R("ui.fullscreen",void 0).catch(()=>{}),Promise.resolve())}}function cn(a){if(a.kind==="res"){let g=m.get(a.id);if(!g)return;m.delete(a.id),a.ok?g.resolve(a.result):g.reject(new Error(a.error));return}if(a.kind!=="event")return;let u=a.name;if(u==="room")f?._receive(a.data);else if(u==="room.connection")a.data?.connected===!1&&f?._disconnected();else if(u==="room.closed"){let g=f;f=null,g?._closed(a.data.reason)}else if(u==="room.moved"){let g=f;f=ze(a.data),g?._closed("moved");for(let v of _)try{v(f)}catch(D){console.error('[onceworlds] error in rooms "moved" listener',D)}}else if(u==="audio")e.apply(a.data),re("audio",a.data);else if(u==="settings")t.apply(a.data);else if(u==="pause"||u==="resume")re(u);else if(u==="controls")N.receive(a.data);else if(u==="input")N.touch=a.data?.touch===!0;else if(u==="reserved")T.receive(a.data)&&re("reserved",T.areas);else if(u==="clip")L?.receive(a.data);else if(u==="snapshot")U?.receive(a.data);else if(u==="debug")for(let g of P)try{g(a.data)}catch(v){console.error("[onceworlds] error in the debugger link",v)}else if(u==="player"){let g=a.data;G().then(v=>{g?.id!==v.player.id||typeof g.name!="string"||(v.player.name=g.name,re("player",v.player))}).catch(()=>{})}}async function R(a,u){await A;let g=c;if(!g)throw new Error("Not connected to Onceworlds");return new Promise((v,D)=>{let b=d++;m.set(b,{resolve:v,reject:D}),g.postMessage({kind:"req",id:b,method:a,params:u})})}function x(a,u){c?.postMessage({kind:"event",name:a,data:u})}function re(a,...u){for(let g of h.get(a)??[])try{g(...u)}catch(v){console.error(`[onceworlds] error in "${a}" listener`,v)}}function ze(a,u=w){let g={send:(b,$)=>x("room.send",{d:b,to:$}),presence:b=>x("room.presence",{d:b}),state:(b,$,C)=>x("room.state",{k:b,v:$,w:C}),hideLobby:b=>x("room.hideLobby",{hidden:b}),team:(b,$)=>x("room.team",{id:b,team:$}),open:b=>x("room.open",{open:b}),result:b=>x("room.result",{result:b}),kick:b=>x("room.kick",{id:b}),voteKick:b=>x("room.votekick",{id:b}),ready:b=>x("room.ready",{ready:b}),unready:()=>x("room.unready",{}),transfer:b=>x("room.transfer",{id:b}),start:b=>x("room.start",b),end:()=>x("room.end",{}),pause:b=>x("room.pause",{paused:b}),admit:b=>x("room.admit",{ids:b}),private:(b,$,C)=>x("room.private",{k:b,v:$,...C?{id:C}:{}}),dataReset:()=>x("room.datareset",{}),leave:()=>{f===v&&(f=null),R("room.leave",void 0).catch(()=>{})}};w=u;let v=new Z(a,g,()=>Date.now()+E,u);Ue(v);let D=Ot(b=>x("room.idle",{idle:b}));return v.on("reconnect",()=>D.idle&&x("room.idle",{idle:!0})),v.on("close",()=>D.stop()),v}function Ue(a){a.on("state",(u,g,v)=>{f===a&&ke(u)&&He._changed(u.slice(H.length),g,v)})}function un(a,u){let g={};for(let[C,W]of Object.entries(ge.all()))g[H+C]=W;let v={room:"solo",you:a.id,host:a.id,players:[{...a,presence:null,team:u.teams?1:0}],state:g,chat:[],teams:u.teams??0,open:!0,now:Date.now(),ranked:!1,kept:!0,owner:a.id},b={..._t(()=>$),state(C,W){ke(C)&&ge.set(C.slice(H.length),W)},dataReset(){for(let C of Object.keys(ge.all()))ge.set(C,null),$._receive({t:"state",k:H+C,v:null,from:""})}},$=new Z(v,b,()=>Date.now()+E,Re(u.settings));return Ue($),$}function G(){return k??(k=A.then(()=>o==="platform"?R("session.get",void 0):$r()).then(a=>(E=Number(a.timeOffset)||0,a))),k}G().catch(()=>{});let Fe={async get(a){return await A,o==="platform"?await R("save.get",{key:a}):pe.get(a)},async set(a,u){await A,o==="platform"?await R("save.set",{key:a,value:u}):pe.set(a,u)},async delete(a){await A,o==="platform"?await R("save.delete",{key:a}):pe.delete(a)},async list(){return await A,o==="platform"?R("save.list",void 0):pe.list()}},Ke={__onceworlds:!0,_debug:s?{send:a=>{c&&x("debug",a)},on:a=>(P.add(a),()=>P.delete(a))}:null,version:en,get mode(){return o},ready:()=>A,player:{get:()=>G().then(a=>a.player),async avatarUrl(a,u="full"){let g=await G().catch(()=>null);if(!g?.platformOrigin)return null;let v=typeof a=="string"&&a?a:g.player.id;return`${g.platformOrigin}/avatar/${encodeURIComponent(v)}${u==="head"?"/head":""}.svg`},async avatar(a){if(await A,o!=="platform")return null;let u=typeof a=="string"&&a?a:(await G()).player.id;return it.test(u)?We(await R("player.avatar",{id:u}).catch(()=>null)):null},async rename(){return await A,o!=="platform"?null:await R("player.rename",void 0).catch(()=>null)?(await G()).player:null}},game:{info:()=>G().then(a=>a.game)},save:Fe,data:{player:Fe,server:He,game:ln},rooms:{async join(a={}){await A;let u=a.party===!0&&f!==null&&o==="platform";if(u||(f?.leave(),f=null),o!=="platform"){let{player:D}=await G();return f=un(D,a),f}let g=await R("room.join",{name:a.name,maxPlayers:a.maxPlayers,mode:a.mode,teams:a.teams,private:a.private,ranked:a.ranked,party:a.party,minPlayers:a.minPlayers,minPlayersPrivate:a.minPlayersPrivate,settings:a.settings,countdown:a.countdown,lobby:a.lobby,...a.ownCountdown===!0?{ownCountdown:!0}:{}}),v=f;return f=ze(g,Re(a.settings)),u&&v?._closed("moved"),f},get current(){return f},on(a,u){return a!=="moved"?()=>{}:(_.add(u),()=>_.delete(u))}},ratings:{async get(a){return await A,o==="platform"?R("ratings.get",{mode:String(a)}):null},async top(a,u={}){return await A,o==="platform"?R("ratings.top",{mode:String(a),limit:u.limit}):[]}},ui:{setMenuPosition(a){R("ui.menuPosition",{position:a}).catch(()=>{})},get reserved(){return T.areas},setLook(a){let{look:u,notes:g}=Yt(a);for(let v of g)M.has(v)||(M.add(v),console.warn(`[onceworlds] ui.setLook: ${v}`));o!=="standalone"&&R("ui.look",u).catch(()=>{})},requestFullscreen(){o==="platform"?R("ui.fullscreen",void 0).catch(()=>{}):document.documentElement.requestFullscreen?.().catch(()=>{})},showInvite(){o==="platform"&&R("ui.invite",void 0).catch(()=>{})},setOrientation(a){a!=="any"&&a!=="landscape"&&a!=="portrait"||o!=="standalone"&&R("ui.orientation",{orientation:a}).catch(()=>{})}},controls:{set(a){N.set(a)},get stick(){return N.stick},pressed(a){return N.pressed(String(a))},presses(a){return N.presses(String(a))},get touch(){return N.touch}},now:()=>Date.now()+E,leaderboards:{async submit(a,u,g={}){if(await A,o!=="platform")return he.submit(String(a),Number(u),g.lowerIsBetter===!0);try{return await R("leaderboards.submit",{board:String(a),score:Number(u),lowerIsBetter:g.lowerIsBetter===!0})}catch(v){return console.warn(`[onceworlds] score for "${a}" was not saved: ${v instanceof Error?v.message:v}`),null}},async top(a,u={}){return await A,o!=="platform"?he.top(String(a)):R("leaderboards.top",{board:String(a),limit:u.limit,lowerIsBetter:u.lowerIsBetter===!0})}},settings:t.settings,audio:{get volume(){return e.volume},get muted(){return e.muted}},badges:{async award(a){if(await A,o!=="platform")return ye.add(String(a));try{return(await R("badges.award",{key:String(a)})).awarded}catch(u){return console.warn(`[onceworlds] badge "${a}" was not awarded: ${u instanceof Error?u.message:u}`),!1}},async list(){return await A,o==="platform"?R("badges.list",void 0):ye.list()},async has(a){return(await Ke.badges.list()).some(u=>u.key===a&&u.earned)}},demo:{get active(){return i},end(){return l??(l=(async()=>(await A,i?o!=="platform"?(console.info("[onceworlds] demo.end(): the end of the demo. On Onceworlds the platform shows its end-of-demo card here."),!0):R("demo.end",void 0).then(a=>a!==!1,()=>!0):!1))().finally(()=>l=null)),l}},env:Er(),async fetch(a,u={}){if(await A,o!=="platform")throw new Error("onceworlds.fetch only works on Onceworlds, where your secrets are stored. Run the game with `onceworlds dev`.");let g=a instanceof Request?a:null,v=new URL(g?g.url:String(a),location.href).href,D=(u.method??g?.method??"GET").toUpperCase(),b={};new Headers(u.headers??g?.headers).forEach((dn,mn)=>{b[mn]=dn});let $=u.body??null;$===null&&g&&D!=="GET"&&D!=="HEAD"&&($=await g.arrayBuffer()),$ instanceof URLSearchParams&&(b["content-type"]??(b["content-type"]="application/x-www-form-urlencoded;charset=UTF-8"),$=$.toString());let C=await R("net.fetch",{url:v,method:D,headers:b,body:await Pr($)}),W=[101,204,205,304].includes(C.status);return new Response(W?null:C.body,{status:C.status,statusText:C.statusText,headers:C.headers})},on(a,u){let g=h.get(a);return g||h.set(a,g=new Set),g.add(u),()=>g.delete(u)}};return Ke}function Er(){let n=globalThis.__ONCEWORLDS_ENV__,e={};if(n&&typeof n=="object")for(let[t,r]of Object.entries(n))typeof r=="string"&&(e[t]=r);return Object.freeze(e)}async function Pr(n){if(n==null)return null;if(typeof n=="string"||n instanceof ArrayBuffer)return n;if(ArrayBuffer.isView(n))return new Uint8Array(n.buffer,n.byteOffset,n.byteLength).slice().buffer;if(n instanceof Blob)return n.arrayBuffer();throw new TypeError("onceworlds.fetch bodies must be a string (e.g. JSON.stringify(...)), bytes or a Blob.")}function tn(n){if(!n)return"";try{let e=new URL(n);return e.origin===location.origin?e.pathname.slice(1)||"index.html":e.href}catch{return n}}function Ar(n){return n instanceof HTMLElement?n.isContentEditable||["INPUT","TEXTAREA","SELECT"].includes(n.tagName):!1}function $r(){let n=B.get("onceworlds:player-id");return n||(n=`local-${Math.random().toString(36).slice(2,10)}`,B.set("onceworlds:player-id",n)),{player:{id:n,name:"Player"},game:{slug:location.hostname.split(".")[0]||"local",name:document.title||"Game",owner:"local"},audio:{volume:1,muted:!1},timeOffset:0}}var nn="onceworlds:scores",he={all(){try{let n=JSON.parse(B.get(nn)??"{}");return n&&typeof n=="object"?n:{}}catch{return{}}},submit(n,e,t){let r=he.all(),s=r[n],o=s===void 0?e:t?Math.min(s,e):Math.max(s,e);return r[n]=o,B.set(nn,JSON.stringify(r)),{best:o,rank:1}},top(n){let e=he.all()[n];if(e===void 0)return{entries:[],me:null};let t={rank:1,player:{id:"local",name:"You"},score:e};return{entries:[t],me:t}}},ne="onceworlds:save:",rn="onceworlds:badges",ye={earned(){try{let n=JSON.parse(B.get(rn)??"{}");return n&&typeof n=="object"?n:{}}catch{return{}}},add(n){let e=ye.earned();return e[n]?!1:(e[n]=Date.now(),B.set(rn,JSON.stringify(e)),!0)},list(){return Object.entries(ye.earned()).map(([n,e])=>({key:n,name:n,description:"",icon:null,earned:!0,earnedAt:e}))}},pe={get(n){let e=B.get(ne+n);if(e===null)return null;try{return JSON.parse(e)}catch{return null}},set(n,e){B.set(ne+n,JSON.stringify(e??null))},delete(n){B.remove(ne+n)},list(){let n=[];try{for(let e=0;e<localStorage.length;e++){let t=localStorage.key(e);t?.startsWith(ne)&&n.push(t.slice(ne.length))}}catch{}return n.sort()}},fe="onceworlds:server:",ge={all(){let n={};try{for(let e=0;e<localStorage.length;e++){let t=localStorage.key(e);if(t?.startsWith(fe))try{n[t.slice(fe.length)]=JSON.parse(localStorage.getItem(t)??"null")}catch{}}}catch{}return n},set(n,e){e==null?B.remove(fe+n):B.set(fe+n,JSON.stringify(e))}},B={get(n){try{return localStorage.getItem(n)}catch{return null}},set(n,e){try{localStorage.setItem(n,e)}catch{}},remove(n){try{localStorage.removeItem(n)}catch{}}};on();})();
|
package/dist/v1/bots.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// ../sdk/src/helpers/random.ts
|
|
2
|
+
function hashString(text) {
|
|
3
|
+
let h1 = 3735928559;
|
|
4
|
+
let h2 = 1103547991;
|
|
5
|
+
for (let i = 0; i < text.length; i++) {
|
|
6
|
+
const c = text.charCodeAt(i);
|
|
7
|
+
h1 = Math.imul(h1 ^ c, 2654435761);
|
|
8
|
+
h2 = Math.imul(h2 ^ c, 1597334677);
|
|
9
|
+
}
|
|
10
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
11
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
12
|
+
return (h2 ^ h1) >>> 0;
|
|
13
|
+
}
|
|
14
|
+
function createRandom(seed) {
|
|
15
|
+
const s = typeof seed === "number" && Number.isFinite(seed) ? Math.floor(seed) >>> 0 : hashString(String(seed));
|
|
16
|
+
let a = 2654435769;
|
|
17
|
+
let b = 608135816;
|
|
18
|
+
let c = 3084996962;
|
|
19
|
+
let d = s;
|
|
20
|
+
const next = () => {
|
|
21
|
+
a >>>= 0;
|
|
22
|
+
b >>>= 0;
|
|
23
|
+
c >>>= 0;
|
|
24
|
+
d >>>= 0;
|
|
25
|
+
let t = a + b | 0;
|
|
26
|
+
a = b ^ b >>> 9;
|
|
27
|
+
b = c + (c << 3) | 0;
|
|
28
|
+
c = c << 21 | c >>> 11;
|
|
29
|
+
d = d + 1 | 0;
|
|
30
|
+
t = t + d | 0;
|
|
31
|
+
c = c + t | 0;
|
|
32
|
+
return (t >>> 0) / 4294967296;
|
|
33
|
+
};
|
|
34
|
+
for (let i = 0; i < 12; i++) next();
|
|
35
|
+
const int = (n) => Number.isFinite(n) && n > 0 ? Math.floor(next() * Math.floor(n)) : 0;
|
|
36
|
+
return {
|
|
37
|
+
next,
|
|
38
|
+
int,
|
|
39
|
+
range: (min, max) => min + next() * (max - min),
|
|
40
|
+
chance: (p) => next() < p,
|
|
41
|
+
pick: (items) => Array.isArray(items) && items.length > 0 ? items[int(items.length)] : void 0,
|
|
42
|
+
shuffle(items) {
|
|
43
|
+
const out = Array.isArray(items) ? [...items] : [];
|
|
44
|
+
for (let i = out.length - 1; i > 0; i--) {
|
|
45
|
+
const j = int(i + 1);
|
|
46
|
+
[out[i], out[j]] = [out[j], out[i]];
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ../sdk/src/helpers/util.ts
|
|
54
|
+
var isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
55
|
+
var said = /* @__PURE__ */ new Map();
|
|
56
|
+
function warn(key, message, level = "warn", error) {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
if (now - (said.get(key) ?? -Infinity) < 5e3) return;
|
|
59
|
+
said.set(key, now);
|
|
60
|
+
if (said.size > 500) said.delete(said.keys().next().value);
|
|
61
|
+
try {
|
|
62
|
+
const text = `[onceworlds] ${message}`;
|
|
63
|
+
if (level === "error") console.error(text, error);
|
|
64
|
+
else if (level === "debug") console.debug(text);
|
|
65
|
+
else console.warn(text);
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function guard(where, fn, fallback) {
|
|
70
|
+
try {
|
|
71
|
+
return fn();
|
|
72
|
+
} catch (error) {
|
|
73
|
+
warn(`threw:${where}`, `${where} threw (the helper carries on)`, "error", error);
|
|
74
|
+
return fallback;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ../sdk/src/helpers/bots.ts
|
|
79
|
+
var BOTS_KEY = "host:ow.bots";
|
|
80
|
+
var NAMES = ["Mox", "Juno", "Pip", "Rook", "Vega", "Tilly", "Bram", "Nix", "Sable", "Otto", "Wren", "Kit", "Lumo", "Dash", "Fern", "Zed"];
|
|
81
|
+
var isMatch = (target) => typeof target._useBots === "function";
|
|
82
|
+
function actionsOf(result) {
|
|
83
|
+
const list = Array.isArray(result) ? result : result ? [result] : [];
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const item of list.slice(0, 8)) {
|
|
86
|
+
if (!isObject(item)) continue;
|
|
87
|
+
const name = item.call ?? item.rpc ?? item.claim;
|
|
88
|
+
if (typeof name === "string") out.push({ name, args: item.args });
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
function createBots(target, options) {
|
|
93
|
+
const match = target && isMatch(target) ? target : null;
|
|
94
|
+
const room = match ? match.room : target;
|
|
95
|
+
let fill = Math.max(0, Math.min(30, Math.floor(options?.fill ?? 4)));
|
|
96
|
+
const every = Math.max(50, options?.every ?? 700);
|
|
97
|
+
const names = options?.names?.length ? options.names.map((n) => String(n).slice(0, 24)) : NAMES;
|
|
98
|
+
const skillRange = options?.skill ?? [0.3, 0.7];
|
|
99
|
+
if (!options || typeof options.think !== "function") warn("bots-think", "createBots(match, { think(view) {...} }) needs a think function", "error");
|
|
100
|
+
if (!options?.host) warn("bots-host", "createBots: without { host } a bot's actions ({ call: 'move' }) have nowhere to go: pass the game's createHost(room)");
|
|
101
|
+
if (match) match._useBots(fill, names, skillRange);
|
|
102
|
+
const nextAt = /* @__PURE__ */ new Map();
|
|
103
|
+
const streams = /* @__PURE__ */ new Map();
|
|
104
|
+
let lastTurn = "";
|
|
105
|
+
let disposed = false;
|
|
106
|
+
const roster = () => {
|
|
107
|
+
const raw = room.state[BOTS_KEY];
|
|
108
|
+
if (!Array.isArray(raw)) return [];
|
|
109
|
+
const humans = room.online.length;
|
|
110
|
+
return raw.filter((b) => isObject(b) && typeof b.id === "string" && b.id.startsWith("bot:") && b.id.length <= 32 && typeof b.name === "string" && b.name.length <= 32 && typeof b.skill === "number" && b.skill >= 0 && b.skill <= 1).slice(0, 30).map((b, i) => ({ id: b.id, name: b.name, bot: true, me: false, index: humans + i, team: 0, away: false, idle: false, left: false, skill: b.skill }));
|
|
111
|
+
};
|
|
112
|
+
function keepRoster() {
|
|
113
|
+
if (match || !room.isHost || disposed) return;
|
|
114
|
+
const want = Math.max(0, Math.min(30 - room.online.length, fill - room.online.length));
|
|
115
|
+
const now = roster();
|
|
116
|
+
if (now.length === want && Array.isArray(room.state[BOTS_KEY])) return;
|
|
117
|
+
const rng = createRandom(`${room.id}:ow.bots`);
|
|
118
|
+
const order = rng.shuffle(names);
|
|
119
|
+
const next = Array.from({ length: want }, (_, i) => {
|
|
120
|
+
const kept = now[i];
|
|
121
|
+
if (kept) return { id: kept.id, name: kept.name, skill: kept.skill };
|
|
122
|
+
const skill = typeof skillRange === "number" ? skillRange : skillRange[0] + rng.next() * (skillRange[1] - skillRange[0]);
|
|
123
|
+
return { id: `bot:${i + 1}`, name: String(order[i % order.length]), skill: Math.round(Math.min(1, Math.max(0, skill)) * 100) / 100 };
|
|
124
|
+
});
|
|
125
|
+
room.setState(BOTS_KEY, next);
|
|
126
|
+
}
|
|
127
|
+
const list = () => match ? match.seats.filter((s) => s.bot) : roster();
|
|
128
|
+
function think() {
|
|
129
|
+
if (disposed) return;
|
|
130
|
+
if (!match) keepRoster();
|
|
131
|
+
const acting = match ? match.isHost && match.phase === "round" && !match.paused : room.isHost;
|
|
132
|
+
if (!acting) {
|
|
133
|
+
nextAt.clear();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
const turn = match?.turn ?? null;
|
|
138
|
+
const turnKey = turn ? `${match.roundId}.${turn.n}` : "";
|
|
139
|
+
if (turnKey !== lastTurn) {
|
|
140
|
+
lastTurn = turnKey;
|
|
141
|
+
if (turn?.seat?.bot) nextAt.set(turn.id, now + every * (0.8 + Math.random() * 0.6));
|
|
142
|
+
}
|
|
143
|
+
for (const bot of list()) {
|
|
144
|
+
const due = nextAt.get(bot.id);
|
|
145
|
+
if (due === void 0) {
|
|
146
|
+
nextAt.set(bot.id, now + every * Math.random());
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (now < due) continue;
|
|
150
|
+
nextAt.set(bot.id, now + every * (0.8 + Math.random() * 0.4) * (1.4 - 0.8 * bot.skill));
|
|
151
|
+
const key = match ? match.roundId : "";
|
|
152
|
+
let stream = streams.get(bot.id);
|
|
153
|
+
if (!stream || stream.key !== key) streams.set(bot.id, stream = { key, random: createRandom(`${room.match.seed ?? 0}:bot:${bot.id}:${key}`) });
|
|
154
|
+
const view = {
|
|
155
|
+
bot,
|
|
156
|
+
skill: bot.skill,
|
|
157
|
+
match,
|
|
158
|
+
get data() {
|
|
159
|
+
return match ? match.data : null;
|
|
160
|
+
},
|
|
161
|
+
state: room.state,
|
|
162
|
+
myTurn: !!turn && turn.id === bot.id,
|
|
163
|
+
random: stream.random
|
|
164
|
+
};
|
|
165
|
+
const result = guard(`bot think (${bot.id})`, () => options.think(view), void 0);
|
|
166
|
+
for (const action of actionsOf(result)) options.host?.callAs({ id: bot.id, name: bot.name }, action.name, action.args);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const timer = setInterval(think, Math.max(50, Math.min(250, Math.floor(every / 4))));
|
|
170
|
+
const offs = [room.on("close", () => api.dispose())];
|
|
171
|
+
if (!match) for (const event of ["join", "leave", "host", "reconnect"]) offs.push(room.on(event, () => keepRoster()));
|
|
172
|
+
const api = {
|
|
173
|
+
get list() {
|
|
174
|
+
return list();
|
|
175
|
+
},
|
|
176
|
+
setFill(n) {
|
|
177
|
+
fill = Math.max(0, Math.min(30, Math.floor(Number(n) || 0)));
|
|
178
|
+
if (match) match._useBots(fill, names, skillRange);
|
|
179
|
+
else keepRoster();
|
|
180
|
+
},
|
|
181
|
+
dispose() {
|
|
182
|
+
if (disposed) return;
|
|
183
|
+
disposed = true;
|
|
184
|
+
clearInterval(timer);
|
|
185
|
+
for (const off of offs) off();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
return api;
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
BOTS_KEY,
|
|
192
|
+
createBots
|
|
193
|
+
};
|