@combos-fun/engine 0.1.2 → 0.1.4-beta.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.
@@ -1 +1 @@
1
- "use strict";var e=require("eventemitter3"),t=require("lodash-es"),s=require("resource-loader");function r(e){return e&&e.__esModule?e:{default:e}}var o,n=r(e);class a extends n.default{get game(){return this.gameObject?.scene?.game}constructor(e){super(),this.started=!1;const t=this.constructor;this.name=t.componentName,this.__componentDefaultParams=e}}exports.OBSERVER_TYPE=void 0,(o=exports.OBSERVER_TYPE||(exports.OBSERVER_TYPE={})).ADD="ADD",o.REMOVE="REMOVE",o.CHANGE="CHANGE";const i={},c={},p={},m={};function h(e,t){i[e.gameObject.id]||(i[e.gameObject.id]={});const s=i[e.gameObject.id],r=e.name+"_"+t.join(",");if(s[r])return s[r];const o=t.length-1;let n=e;for(let e=0;e<o;e++){if(void 0===n[t[e]]||null===n[t[e]])return{property:void 0,key:void 0};n=n[t[e]]}return s[r]={property:n,key:t[o]},s[r]}function u({systemName:e,componentName:t,component:s,prop:r,type:o,gameObject:n}){c[e]?.componentObserver?.add({component:s,prop:r,type:o,componentName:t,gameObject:n})}const l="GameObject";function d(e,t,s=exports.OBSERVER_TYPE.CHANGE){if(!e)return;const r={prop:[t],deep:!1};for(const o in p){const n=p[o]?.[l];n?.some(e=>e.prop[0]===t)&&u({systemName:o,componentName:l,component:void 0,gameObject:e,prop:r,type:s})}}function y({obj:e,key:s,prop:r,component:o,componentName:n}){if(void 0!==e)if(s in e){if(Object.defineProperty(e,`_${s}`,{enumerable:!1,writable:!0,value:e[s]}),r.deep&&t.isObject(e[s]))for(const t of Object.keys(e[s]))y({obj:e[s],key:t,prop:r,component:o,componentName:n});Object.defineProperty(e,s,{enumerable:!0,set(a){e[`_${s}`]!==a&&(e[`_${s}`]=a,function({prop:e,component:s,componentName:r}){for(const o in p){const n=(p[o]||{})[r];n&&(n.findIndex(s=>t.isEqual(s,e))>-1&&u({systemName:o,componentName:r,component:s,prop:e,type:exports.OBSERVER_TYPE.CHANGE}))}}({prop:r,component:o,componentName:n}))},get:()=>e[`_${s}`]})}else console.error(`prop ${s} not in component: ${n}, Can not observer`)}function f(e,t=e.name){for(const s in p){(p[s]||{})[t]&&c[s]?.componentObserver?.add({component:e,type:exports.OBSERVER_TYPE.REMOVE,componentName:t})}!function(e){e.gameObject&&delete i[e.gameObject.id]}(e)}let g=0;class E{constructor(e){this._parent=null,this._componentCache={},this.children=[],this.inScene=!1,this.components=[],this.destroyed=!1,this._name=e,this.id=++g}get parent(){return this._parent}get name(){return this._name}set scene(e){if(this._scene===e)return;const t=this._scene;this._scene=e;for(const t of this.children)t.scene=e;e?e.addGameObject(this):t&&t.removeGameObject(this)}get scene(){return this._scene}addChild(e){if(e&&e!==this){if(!(e instanceof E))throw new Error("addChild only receive GameObject");if(this.destroyed)throw new Error(`gameObject '${this.name}' has been destroy`);if(e.parent===this){const t=this.children.indexOf(e);return t>-1&&this.children.splice(t,1),void this.children.push(e)}e.parent&&e.parent.removeChild(e),e._parent=this,this.children.push(e),e.scene=this.scene,d(e,"parent",exports.OBSERVER_TYPE.CHANGE)}}removeChild(e){if(!(e instanceof E&&e.parent&&e.parent===this))return e;const t=this.children.indexOf(e);return t>-1&&this.children.splice(t,1),e._parent=null,e.scene=null,d(e,"parent",exports.OBSERVER_TYPE.CHANGE),e}addComponent(e,t){if(this.destroyed)return;const s=function(e){return e instanceof a?e.name:e instanceof Function?e.componentName:void 0}(e);if(this._componentCache[s])return;let r;if(e instanceof Function)r=new e(t);else{if(!(e instanceof a))throw new Error("addComponent recieve Component and Component Constructor");r=e}if(r.gameObject)throw new Error(`component has been added on gameObject ${r.gameObject.name}`);return r.gameObject=this,r.init&&r.init(r.__componentDefaultParams),function(e,t=e.name){for(const s in p)(p[s]||{})[t]&&c[s]?.componentObserver?.add({component:e,type:exports.OBSERVER_TYPE.ADD,componentName:t})}(r,r.name),function(e,t=e.name){if(t&&m[t]){if(!(e&&(s=e,s&&s.constructor&&"componentName"in s.constructor)))throw new Error("component param must be an instance of Component");var s;if(!e.gameObject||!e.gameObject.id)throw new Error("component should be add to a gameObject");for(const s of m[t]){const{property:r,key:o}=h(e,s.prop);y({obj:r,key:o,prop:s,component:e,componentName:t})}}}(r,r.name),this.components.push(r),this._componentCache[s]=r,r.awake&&r.awake(),r}removeComponent(e){let t;return"string"==typeof e?t=e:e instanceof a?t=e.name:e.componentName&&(t=e.componentName),this._removeComponent(t)}_removeComponent(e){const t=this.components.findIndex(({name:t})=>t===e);if(-1===t)return;const s=this.components.splice(t,1)[0];return delete this._componentCache[e],delete s.__componentDefaultParams,s.onDestroy&&s.onDestroy(),f(s,e),s.gameObject=void 0,s}getComponent(e){let t;return"string"==typeof e?t=e:e instanceof a?t=e.name:e.componentName&&(t=e.componentName),void 0!==this._componentCache[t]?this._componentCache[t]:void 0}remove(){if(this.parent)return this.parent.removeChild(this)}destroy(){if(this.destroyed)console.error("Cannot destroy gameObject that have already been destroyed.");else{Array.from(this.children).forEach(e=>{e.destroy()}),this.remove(),this.children.length=0;for(const e in this._componentCache)this._removeComponent(e);this.components.length=0,this.destroyed=!0}}}class S{constructor(){this.events=[]}add({component:e,prop:s,type:r,componentName:o,gameObject:n}){const a=n??e?.gameObject;if(r===exports.OBSERVER_TYPE.REMOVE){const t=t=>e?t.component===e:t.gameObject===a&&t.componentName===o;if(this.events.find(e=>t(e)&&e.type===exports.OBSERVER_TYPE.ADD))return void(this.events=this.events.filter(e=>!t(e)));this.events=this.events.filter(e=>!t(e))}const i=this.events.findIndex(n=>n.gameObject===a&&n.component===e&&n.componentName===o&&t.isEqual(n.prop,s)&&n.type===r);i>-1&&this.events.splice(i,1),this.events.push({gameObject:a,component:e,prop:s,type:r,componentName:o})}getChanged(){return this.events}get changed(){return this.events}clear(){const e=this.events;return this.events=[],e}}class b{constructor(e){this.started=!1,this.componentObserver=new S,this.__systemDefaultParams=e;const t=this.constructor;this.name=t.systemName}destroy(){this.componentObserver=null,this.__systemDefaultParams=null,this.onDestroy?.()}}const T=function(){let e=null;return e=Date.now?Date.now:()=>(new Date).getTime(),e}(),R={originTime:0,playbackRate:1};class O{constructor(e,t){e instanceof O&&(t=e,e={}),e=Object.assign({},R,e),t&&(this._parent=t),this._createTime=T(),this._timeMark=[{globalTime:this.globalTime,localTime:-e.originTime,entropy:-e.originTime,playbackRate:e.playbackRate,globalEntropy:0}],this._parent&&(this._timeMark[0].globalEntropy=this._parent.entropy),this._playbackRate=e.playbackRate}get globalTime(){return this.parent?this.parent.currentTime:T()-this._createTime}get parent(){return this._parent}get lastTimeMark(){return this._timeMark[this._timeMark.length-1]}markTime({time:e=this.currentTime,entropy:t=this.entropy,playbackRate:s=this.playbackRate}={}){const r={globalTime:this.globalTime,localTime:e,entropy:t,playbackRate:s,globalEntropy:this.globalEntropy};this._timeMark.push(r)}get currentTime(){const{localTime:e,globalTime:t}=this.lastTimeMark;return e+(this.globalTime-t)*this.playbackRate}set currentTime(e){this.markTime({time:e})}get globalEntropy(){return this._parent?this._parent.entropy:this.globalTime}get entropy(){const{entropy:e,globalEntropy:t}=this.lastTimeMark;return e+Math.abs((this.globalEntropy-t)*this.playbackRate)}set entropy(e){if(this.entropy>e){const t=this.seekTimeMark(e);this._timeMark.length=t+1}this.markTime({entropy:e})}fork(e){return new O(e,this)}seekGlobalTime(e){const t=this.seekTimeMark(e),s=this._timeMark[t],{entropy:r,playbackRate:o,globalTime:n}=s;return n+(e-r)/Math.abs(o)}seekLocalTime(e){const t=this.seekTimeMark(e),s=this._timeMark[t],{localTime:r,entropy:o,playbackRate:n}=s;return n>0?r+(e-o):r-(e-o)}seekTimeMark(e){const t=this._timeMark;let s=0,r=t.length-1;if(e<=t[s].entropy)return s;if(e>=t[r].entropy)return r;let o=Math.floor((s+r)/2);for(;o>s&&o<r;){if(e===t[o].entropy)return o;e<t[o].entropy?r=o:e>t[o].entropy&&(s=o),o=Math.floor((s+r)/2)}return s}get playbackRate(){return this._playbackRate}set playbackRate(e){e!==this.playbackRate&&(this.markTime({playbackRate:e}),this._playbackRate=e)}get paused(){if(0===this.playbackRate)return!0;let e=this.parent;for(;e;){if(0===e.playbackRate)return!0;e=e.parent}return!1}}const _={autoStart:!0,frameRate:60};class x{constructor(e){e=Object.assign({},_,e),this._frameCount=0,this._frameDuration=1e3/e.frameRate,this.autoStart=e.autoStart,this.frameRate=e.frameRate,this.timeline=new O({originTime:0,playbackRate:1}),this._lastFrameTime=this.timeline.currentTime,this._tickers=new Set,this._requestId=null,this._ticker=()=>{this._started&&(this._requestId=requestAnimationFrame(this._ticker),this.update())},this.autoStart&&this.start()}update(){const e=this.timeline.currentTime,t=e-this._lastFrameTime;if(t>=this._frameDuration){const s=e-t%this._frameDuration,r=s-this._lastFrameTime;this._lastFrameTime=s;const o={deltaTime:r,delta:r,time:s,currentTime:s,frameCount:++this._frameCount,fps:Math.round(1e3/r)};for(const e of this._tickers)"function"==typeof e&&e(o)}}add(e){this._tickers.add(e)}remove(e){this._tickers.delete(e)}start(){this._started||(this._started=!0,this.timeline.playbackRate=1,this._requestId=requestAnimationFrame(this._ticker))}pause(){this._started=!1,this.timeline.playbackRate=0}setPlaybackRate(e){this.timeline.playbackRate=e}}class L extends E{constructor(e){super(e),this.gameObjects=[],this.scene=this}addGameObject(e){this.gameObjects.push(e),e.inScene||(e.inScene=!0,d(e,"inScene",exports.OBSERVER_TYPE.CHANGE))}removeGameObject(e){const t=this.gameObjects.indexOf(e);-1!==t&&(e.inScene&&(e.inScene=!1,d(e,"inScene",exports.OBSERVER_TYPE.CHANGE)),this.gameObjects.splice(t,1))}destroy(){this.scene=null,super.destroy(),this.gameObjects=null,this.canvas=null}}const k="0.1.2",v="combos-game:plugin-init-success",w="combos-game:ready",A="combos-game:set-playing",M="combos-game:state-changed";function C(e,t){if("undefined"!=typeof window&&window.parent&&window.parent!==window)try{window.parent.postMessage(e,t)}catch{}}function N(e,t="*"){C({type:v,systemName:e.systemName,engineVersion:k,...e.packageName?{packageName:e.packageName}:{},...e.packageVersion?{packageVersion:e.packageVersion}:{}},t)}function j(e,t="*"){C({type:w,engineVersion:k,...void 0===e?{}:{error:e instanceof Error?e.message:String(e)}},t)}function X(e,t="*"){C({type:M,playing:e.playing,started:e.started},t)}function P(e){if(!e||"object"!=typeof e)return null;const t=e;return t.type!==A||"boolean"!=typeof t.playing?null:t.playing}const I=["knoffice.tech","converge.ai"];function D(e,t){if(t.includes("*"))return!0;if(0===t.length)return!1;let s;try{s=new URL(e)}catch{return!1}const r=e.toLowerCase(),o=s.hostname.toLowerCase();for(const e of t){const t=e.trim();if(!t)continue;if(t.includes("://")){if(r===t.toLowerCase())return!0;continue}const s=t.toLowerCase().replace(/^\./,"");if(o===s||o.endsWith(`.${s}`))return!0}return!1}function V(e){if(!e?.length)return[...I];if(e.includes("*"))return["*"];const t=new Set,s=[];for(const r of[...I,...e]){const e=r.trim();e&&!t.has(e)&&(t.add(e),s.push(e))}return s}function G(e){if("systemName"in e){const t=e.systemName;if("string"==typeof t)return t}return"UnknownSystem"}function $(e){const t=e;return{packageName:"string"==typeof t.packageName?t.packageName:void 0,packageVersion:"string"==typeof t.packageVersion?t.packageVersion:void 0}}var B;exports.LOAD_SCENE_MODE=void 0,(B=exports.LOAD_SCENE_MODE||(exports.LOAD_SCENE_MODE={})).SINGLE="SINGLE",B.MULTI_CANVAS="MULTI_CANVAS";const U=e=>{if((e instanceof b||e instanceof a)&&!e.started){e.started=!0;try{e.start&&e.start()}catch(t){e instanceof a?console.error(`${function(e){if("componentName"in e){const t=e.componentName;if("string"==typeof t)return t}return"UnknownComponent"}(e.constructor)} start error`,t):console.error(`${G(e.constructor)} start error`,t)}}};class H extends n.default{constructor({systems:e,frameRate:t=60,autoStart:s=!0,needScene:r=!0,onSystemsBootstrapComplete:o,pluginInitNotifyTargetOrigin:n="*",allowedMessageOrigins:a}={}){if(super(),this.playing=!1,this.started=!1,this.multiScenes=[],this.systems=[],this.pluginInitNotifyTargetOrigin="*",this.allowedMessageOrigins=V(),this.onHostMessage=e=>{if(!D(e.origin,this.allowedMessageOrigins))return;const t=P(e.data);null!==t&&this.setPlaying(t)},this.pluginInitNotifyTargetOrigin=n,this.allowedMessageOrigins=V(a),this.ticker=new x({autoStart:!1,frameRate:t}),this.initTicker(),"undefined"!=typeof window&&window.addEventListener("message",this.onHostMessage),e&&e.length){const t=[...e];this.bootstrapSystemsAndMaybeStart(t,r,s,o)}else o?.(this),this.notifyReady()}setPlaying(e){e?this.started?this.resume():this.start():this.pause()}notifyReady(e){j(e,this.pluginInitNotifyTargetOrigin)}notifyStateChanged(){X({playing:this.playing,started:this.started},this.pluginInitNotifyTargetOrigin)}async bootstrapSystemsAndMaybeStart(e,t,s,r){let o;try{for(const t of e)await this.addSystem(t);t&&this.loadScene({scene:new L("scene")}),s&&this.start()}catch(e){o=e,console.error("Game bootstrap failed",e)}finally{r?.(this,o),this.notifyReady(o)}}get scene(){return this._scene}set scene(e){this._scene=e}get gameObjects(){return(e=>{const t=e?.scene?.gameObjects||[],s=e?.multiScenes.map(({gameObjects:e})=>e);let r=[];for(const e of s)r=[...r,...e];return[...t,...r]})(this)}async addSystem(e,s){let r;if(e instanceof Function)r=new e(s);else{if(!(e instanceof b))return void console.warn("can only add System");r=e}if(!this.systems.find(e=>e.constructor===r.constructor)){r.game=this,r.init&&await Promise.resolve(r.init(r.__systemDefaultParams)),N({systemName:G(r.constructor),...$(r.constructor)},this.pluginInitNotifyTargetOrigin),function(e,t){p[t.systemName]=t.observerInfo,c[t.systemName]=e}(r,r.constructor),function(e){const s=[];e instanceof Array?s.push(...e):s.push(e);for(const e of s)for(const s in e.observerInfo){m[s]=m[s]||[];const r=m[s];for(const o of e.observerInfo[s])-1===r.findIndex(e=>t.isEqual(e,o))&&m[s].push(o)}}(r.constructor);try{r.awake&&r.awake()}catch(e){console.error(`${G(r.constructor)} awake error`,e)}return this.systems.push(r),r}console.warn(`${G(r.constructor)} System has been added`)}removeSystem(e){if(!e)return;let t=-1;"string"==typeof e?t=this.systems.findIndex(t=>t.name===e):e instanceof Function?t=this.systems.findIndex(t=>t.constructor===e):e instanceof b&&(t=this.systems.findIndex(t=>t===e)),t>-1&&(this.systems[t].destroy&&this.systems[t].destroy(),this.systems.splice(t,1))}getSystem(e){return this.systems.find(t=>"string"==typeof e?t.name===e:t instanceof e)}pause(){this.playing&&(this.playing=!1,this.ticker.pause(),this.triggerPause(),this.notifyStateChanged())}start(){this.playing||(this.playing=!0,this.started=!0,this.ticker.start(),this.notifyStateChanged())}resume(){this.playing||(this.playing=!0,this.ticker.start(),this.triggerResume(),this.notifyStateChanged())}initTicker(){this.ticker.add(e=>{this.scene&&((e,t=[])=>{for(const s of t)for(const t of s.components)try{U(t),t.update&&t.update(e)}catch(e){console.error(`gameObject: ${s.name} ${t.name} update error`,e)}for(const s of t)for(const t of s.components)try{t.lateUpdate&&t.lateUpdate(e)}catch(e){console.error(`gameObject: ${s.name} ${t.name} lateUpdate error`,e)}})(e,this.gameObjects);for(const t of this.systems)try{U(t),t.update&&t.update(e)}catch(e){console.error(`${G(t.constructor)} update error`,e)}for(const t of this.systems)try{t.lateUpdate&&t.lateUpdate(e)}catch(e){console.error(`${G(t.constructor)} lateUpdate error`,e)}})}triggerResume(){(e=>{for(const t of e)for(const e of t.components)try{e.onResume&&e.onResume()}catch(s){console.error(`gameObject: ${t.name}, ${e.name}, onResume error`,s)}})(this.gameObjects);for(const e of this.systems)try{e.onResume&&e.onResume()}catch(t){console.error(`${G(e.constructor)}, onResume error`,t)}}triggerPause(){(e=>{for(const t of e)for(const e of t.components)try{e.onPause&&e.onPause()}catch(s){console.error(`gameObject: ${t.name}, ${e.name}, onPause error`,s)}})(this.gameObjects);for(const e of this.systems)try{e.onPause&&e.onPause()}catch(t){console.error(`${G(e.constructor)}, onPause error`,t)}}destroySystems(){for(const e of[...this.systems])this.removeSystem(e);this.systems.length=0}destroy(){"undefined"!=typeof window&&window.removeEventListener("message",this.onHostMessage),this.removeAllListeners(),this.pause(),this.scene.destroy(),this.destroySystems(),this.ticker=null,this.scene=null,this.canvas=null,this.multiScenes=null}loadScene({scene:e,mode:t=exports.LOAD_SCENE_MODE.SINGLE,params:s={}}){if(e){switch(e.game=this,t){case exports.LOAD_SCENE_MODE.SINGLE:this.scene=e;break;case exports.LOAD_SCENE_MODE.MULTI_CANVAS:this.multiScenes.push(e)}this.emit("sceneChanged",{scene:e,mode:t,params:s})}}}function Y(e,t){e.constructor.IDEProps||(e.constructor.IDEProps=[]),e.constructor.IDEProps.push(t)}function F(e={}){return function(t){if(!t.observerInfo){for(const t in e)for(const s in e[t]){let r;"string"==typeof e[t][s]&&(e[t][s]=[e[t][s]]),Array.isArray(e[t][s])&&(r={prop:e[t][s],deep:!1},e[t][s]=r),r=e[t][s],"string"==typeof r.prop&&(r.prop=[r.prop])}t.observerInfo=e}}}var q;exports.LOAD_EVENT=void 0,(q=exports.LOAD_EVENT||(exports.LOAD_EVENT={})).START="start",q.PROGRESS="progress",q.LOADED="loaded",q.COMPLETE="complete",q.ERROR="error";class z extends n.default{constructor({resource:e,resourceTotal:t}){super(),this.progress=0,this.resourceTotal=0,this.resourceLoadedCount=0,this.resource=e,this.resourceTotal=t,0===t&&this.resource.emit(exports.LOAD_EVENT.COMPLETE,this)}onStart(){this.resource.emit(exports.LOAD_EVENT.START,this)}onProgress(e){this.resourceLoadedCount++,this.progress=Math.floor(this.resourceLoadedCount/this.resourceTotal*100)/100,e.success?this.resource.emit(exports.LOAD_EVENT.LOADED,this,e):this.resource.emit(exports.LOAD_EVENT.ERROR,this,e),this.resource.emit(exports.LOAD_EVENT.PROGRESS,this,e),this.resourceLoadedCount===this.resourceTotal&&this.resource.emit(exports.LOAD_EVENT.COMPLETE,this)}}const J={image:"png",json:"json",tex:"tex",ske:"ske",audio:"audio",video:"video",model:"glb",mtl:"mtl",bin:"bin",vertex:"vert",fragment:"frag"},W={png:"png",jpg:"jpg",jpeg:"jpeg",webp:"webp",json:"json",tex:"tex",ske:"ske",mp3:"audio",wav:"audio",aac:"audio",ogg:"audio",mp4:"video",webm:"video",glb:"glb",gltf:"gltf",fbx:"fbx",obj:"obj",mtl:"mtl",stl:"stl",dae:"dae",ply:"ply",bin:"bin",vert:"vert",frag:"frag",glsl:"glsl"};function K(e,t){return W[function(e){const t=e.split("?")[0].split("#")[0],s=t.lastIndexOf(".");return s<0?"":t.slice(s+1).toLowerCase()}(e)]||J[t]||"png"}function Q(e,t,s){if("string"==typeof t){const r=t.trim();if(!r)throw Z(s,e);return{type:K(r,e),url:r}}if(!t||"object"!=typeof t||Array.isArray(t))throw new Error(`Resource "${s}" src.${e} must be a url string or { type, url }.`);const r=t;if("data"===r.type)return r;const o="string"==typeof r.url?r.url.trim():"";if(!o)throw Z(s,e);return{...r,type:r.type||K(o,e),url:o}}function Z(e,t){return new Error(`Resource "${e}" src.${t}.url is required. Use { type, url } (e.g. { type: "webp", url: "https://..." }). A flat path string is also accepted and will be normalized.`)}function ee(e,t,s="(unnamed)"){if(null==e)throw new Error(`Resource "${s}" src is required.`);if("string"==typeof e){const r=function(e){return"AUDIO"===e?"audio":"VIDEO"===e?"video":"MODEL"===e||"GLB"===e?"model":"SHADER"===e?"fragment":"image"}(t);return{[r]:Q(r,e,s)}}if("object"!=typeof e||Array.isArray(e))throw new Error(`Resource "${s}" src must be a url string or a slot object.`);const r={};for(const[t,o]of Object.entries(e))null!=o&&(r[t]=Q(t,o,s));if(0===Object.keys(r).length)throw new Error(`Resource "${s}" src has no loadable slots.`);return r}const te={AbstractLoadStrategy:s.AbstractLoadStrategy,AudioLoadStrategy:s.AudioLoadStrategy,ImageLoadStrategy:s.ImageLoadStrategy,XhrResponseType:s.XhrResponseType,MediaElementLoadStrategy:s.MediaElementLoadStrategy,VideoLoadStrategy:s.VideoLoadStrategy,XhrLoadStrategy:s.XhrLoadStrategy,Loader:s.Loader,Resource:s.Resource,ResourceType:s.ResourceType,ResourceState:s.ResourceState};function se(e){const t=e.frame;return t&&"object"==typeof t?{...e,frame:t,rotated:Boolean(e.rotated),trimmed:Boolean(e.trimmed),spriteSourceSize:e.spriteSourceSize,sourceSize:e.sourceSize}:null}function re(e,t,s){for(const r of e){if(!r||"object"!=typeof r)continue;const e=r,o=String(e.filename||e.name||"");if(!o)continue;const n=se(e);if(!n)continue;t[o]=n;const a=o.lastIndexOf("/");if(a>0){const e=o.slice(0,a);s[e]||(s[e]=[]),s[e].includes(o)||s[e].push(o)}}}var oe;exports.RESOURCE_TYPE=void 0,(oe=exports.RESOURCE_TYPE||(exports.RESOURCE_TYPE={})).IMAGE="IMAGE",oe.SPRITE="SPRITE",oe.SPRITE_ANIMATION="SPRITE_ANIMATION",oe.AUDIO="AUDIO",oe.VIDEO="VIDEO",oe.GLB="GLB",oe.MODEL="MODEL",oe.SHADER="SHADER",s.XhrLoadStrategy.setExtensionXhrType("json",s.XhrResponseType.Json),s.XhrLoadStrategy.setExtensionXhrType("tex",s.XhrResponseType.Json),s.XhrLoadStrategy.setExtensionXhrType("ske",s.XhrResponseType.Json),s.XhrLoadStrategy.setExtensionXhrType("mp3",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("wav",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("aac",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("ogg",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("glb",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("gltf",s.XhrResponseType.Json),s.XhrLoadStrategy.setExtensionXhrType("fbx",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("stl",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("ply",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("bin",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("obj",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("mtl",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("dae",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("vert",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("frag",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("glsl",s.XhrResponseType.Text);const ne={png:s.ImageLoadStrategy,jpg:s.ImageLoadStrategy,jpeg:s.ImageLoadStrategy,webp:s.ImageLoadStrategy,json:s.XhrLoadStrategy,tex:s.XhrLoadStrategy,ske:s.XhrLoadStrategy,audio:s.XhrLoadStrategy,video:s.VideoLoadStrategy,glb:s.XhrLoadStrategy,gltf:s.XhrLoadStrategy,fbx:s.XhrLoadStrategy,obj:s.XhrLoadStrategy,mtl:s.XhrLoadStrategy,stl:s.XhrLoadStrategy,dae:s.XhrLoadStrategy,ply:s.XhrLoadStrategy,bin:s.XhrLoadStrategy,vert:s.XhrLoadStrategy,frag:s.XhrLoadStrategy,glsl:s.XhrLoadStrategy};class ae extends n.default{constructor(e){super(),this.timeout=6e3,this.preProcessResourceHandlers=[],this.resourcesMap={},this.makeInstanceFunctions={},this.destroyInstanceFunctions={},this.promiseMap={},this.loaders=[],e&&"number"==typeof e.timeout&&(this.timeout=e.timeout)}loadConfig(e){this.addResource(e),this.preload()}loadSingle(e){return this.addResource([e]),this.getResource(e.name)}addResource(e){if(!e||e.length<1)console.warn("no resources");else for(const t of e)this.resourcesMap[t.name]?console.warn(t.name+" was already added"):this.resourcesMap[t.name]={...t,src:ee(t.src,String(t.type),t.name),data:{}}}addPreProcessResourceHandler(e){this.preProcessResourceHandlers.push(e)}removePreProcessResourceHandler(e){this.preProcessResourceHandlers.splice(this.preProcessResourceHandlers.indexOf(e),1)}preload(){const e=[];for(const t in this.resourcesMap){const s=this.resourcesMap[t];!s.preload||s.complete||this.promiseMap[t]||e.push(s.name)}this.progress=new z({resource:this,resourceTotal:e.length}),this.loadResource({names:e,preload:!0})}async getResource(e){return this.loadResource({names:[e]}),this.promiseMap[e]||Promise.resolve({})}async instance(e){const t=this.resourcesMap[e];return this.makeInstanceFunctions[t.type]&&await this.makeInstanceFunctions[t.type](t)}async destroy(e){await this._destroy(e)}async _destroy(e,t=!1){const s=this.resourcesMap[e];if(s){if(!t)try{this.destroyInstanceFunctions[s.type]&&await this.destroyInstanceFunctions[s.type](s)}catch(e){console.warn(`destroy resource ${s.name} error with '${e.message}'`)}delete this.promiseMap[e],s.data={},s.complete=!1,s.instance=void 0}}registerResourceType(e,t=e){if(exports.RESOURCE_TYPE[e])throw new Error(`The type ${e} already exists in RESOURCE_TYPE`);exports.RESOURCE_TYPE[e]=t}registerInstance(e,t){this.makeInstanceFunctions[e]=t}registerDestroy(e,t){this.destroyInstanceFunctions[e]=t}loadResource({names:e=[],preload:t=!1}){const s=e.filter(e=>!this.promiseMap[e]&&this.resourcesMap[e]);if(!s.length)return;const r={},o=this.getLoader(t);s.forEach(e=>{this.promiseMap[e]=new Promise(t=>r[e]=t);const s=this.resourcesMap[e];for(const e of this.preProcessResourceHandlers)e(s);s.src=ee(s.src,String(s.type),s.name);for(const n in s.src){const a=s.src[n].type;"data"===a?(s.data[n]=s.src[n].data,this.doComplete(e,r[e],t)):o.add({url:s.src[n].url,name:`${s.name}_${n}`,strategy:ne[a],metadata:{key:n,name:s.name,resolves:r}})}}),o.load()}async doComplete(e,t,s=!1){const r=this.resourcesMap[e],o={name:e,resource:this.resourcesMap[e],success:!0};if(this.checkAllLoaded(e))try{r.instance=await this.instance(e),r.complete=!0,s&&this.progress.onProgress(o),t(r)}catch(e){console.error(e),r.complete=!1,s&&(o.errMsg=e.message,o.success=!1,this.progress.onProgress(o)),t({})}}checkAllLoaded(e){const t=this.resourcesMap[e];return Array.from(Object.keys(t.src)).every(e=>t.data[e])}getLoader(e=!1){let t=this.loaders.find(({loading:e})=>!e);return t||(t=new s.Loader,this.loaders.push(t)),e&&t.onStart.once(()=>{this.progress.onStart()}),t.onLoad.add((t,s)=>{this.onLoad({preload:e,resource:s})}),t.onError.add((t,s,r)=>{this.onError({errMsg:t,resource:r,preload:e})}),t.onComplete.once(()=>{t.onLoad.detachAll(),t.onError.detachAll(),t.reset()}),t}async onLoad({preload:e=!1,resource:t}){const{metadata:{key:s,name:r,resolves:o},data:n}=t;this.resourcesMap[r].data[s]=n,this.doComplete(r,o[r],e)}async onError({errMsg:e,preload:t=!1,resource:s}){const{metadata:{name:r,resolves:o}}=s;if(this._destroy(r,!0),o[r]({}),t){const t={name:r,resource:this.resourcesMap[r],success:!1,errMsg:e};this.progress.onProgress(t)}}}const ie=new ae;ie.registerInstance(exports.RESOURCE_TYPE.SHADER,e=>({vertex:"string"==typeof e.data?.vertex?e.data.vertex:"",fragment:"string"==typeof e.data?.fragment?e.data.fragment:""}));const ce={IDEProp:Y,componentObserver:F};console.log(`@combos-fun/engine version: ${k}`),exports.COMBOS_GAME_PLUGIN_INIT_SUCCESS=v,exports.COMBOS_GAME_READY=w,exports.COMBOS_GAME_SET_PLAYING=A,exports.COMBOS_GAME_STATE_CHANGED=M,exports.Component=a,exports.DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES=I,exports.Game=H,exports.GameObject=E,exports.IDEProp=Y,exports.RESOURCE_TYPE_STRATEGY=ne,exports.Scene=L,exports.System=b,exports.componentObserver=F,exports.decorators=ce,exports.isAllowedMessageOrigin=D,exports.mergeAllowedMessageOrigins=V,exports.normalizeResourceSrc=ee,exports.normalizeSpritesheetData=function(e){if(!e||"object"!=typeof e)return{frames:{},animations:{},meta:{scale:1}};const t=e;if(function(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}(t.frames))return{...t,frames:t.frames,animations:{...t.animations||{}},meta:{scale:1,...t.meta||{}}};const s={},r={...t.animations||{}};if(Array.isArray(t.textures)){for(const e of t.textures)e&&"object"==typeof e&&Array.isArray(e.frames)&&re(e.frames,s,r);const e=t.textures[0]||{},o=e.scale??t.meta?.scale??1;return{frames:s,animations:r,meta:{scale:"number"==typeof o||"string"==typeof o?o:1,image:"string"==typeof e.image?e.image:void 0,size:e.size,format:e.format,...t.meta||{}}}}return Array.isArray(t.frames)?(re(t.frames,s,r),{frames:s,animations:r,meta:{scale:1,...t.meta||{}}}):{frames:{},animations:r,meta:{scale:1,...t.meta||{}}}},exports.parseSetPlayingMessage=P,exports.postParentGameReady=j,exports.postParentGameState=X,exports.postParentPluginInitSuccess=N,exports.resource=ie,exports.resourceLoader=te,exports.version=k;
1
+ "use strict";var e=require("eventemitter3"),t=require("lodash-es"),s=require("resource-loader");function r(e){return e&&e.__esModule?e:{default:e}}var o,n=r(e);class a extends n.default{get game(){return this.gameObject?.scene?.game}constructor(e){super(),this.started=!1;const t=this.constructor;this.name=t.componentName,this.__componentDefaultParams=e}}exports.OBSERVER_TYPE=void 0,(o=exports.OBSERVER_TYPE||(exports.OBSERVER_TYPE={})).ADD="ADD",o.REMOVE="REMOVE",o.CHANGE="CHANGE";const i={},c={},p={},m={};function h(e,t){i[e.gameObject.id]||(i[e.gameObject.id]={});const s=i[e.gameObject.id],r=e.name+"_"+t.join(",");if(s[r])return s[r];const o=t.length-1;let n=e;for(let e=0;e<o;e++){if(void 0===n[t[e]]||null===n[t[e]])return{property:void 0,key:void 0};n=n[t[e]]}return s[r]={property:n,key:t[o]},s[r]}function u({systemName:e,componentName:t,component:s,prop:r,type:o,gameObject:n}){c[e]?.componentObserver?.add({component:s,prop:r,type:o,componentName:t,gameObject:n})}const l="GameObject";function d(e,t,s=exports.OBSERVER_TYPE.CHANGE){if(!e)return;const r={prop:[t],deep:!1};for(const o in p){const n=p[o]?.[l];n?.some(e=>e.prop[0]===t)&&u({systemName:o,componentName:l,component:void 0,gameObject:e,prop:r,type:s})}}function y({obj:e,key:s,prop:r,component:o,componentName:n}){if(void 0!==e)if(s in e){if(Object.defineProperty(e,`_${s}`,{enumerable:!1,writable:!0,value:e[s]}),r.deep&&t.isObject(e[s]))for(const t of Object.keys(e[s]))y({obj:e[s],key:t,prop:r,component:o,componentName:n});Object.defineProperty(e,s,{enumerable:!0,set(a){e[`_${s}`]!==a&&(e[`_${s}`]=a,function({prop:e,component:s,componentName:r}){for(const o in p){const n=(p[o]||{})[r];n&&(n.findIndex(s=>t.isEqual(s,e))>-1&&u({systemName:o,componentName:r,component:s,prop:e,type:exports.OBSERVER_TYPE.CHANGE}))}}({prop:r,component:o,componentName:n}))},get:()=>e[`_${s}`]})}else console.error(`prop ${s} not in component: ${n}, Can not observer`)}function f(e,t=e.name){for(const s in p){(p[s]||{})[t]&&c[s]?.componentObserver?.add({component:e,type:exports.OBSERVER_TYPE.REMOVE,componentName:t})}!function(e){e.gameObject&&delete i[e.gameObject.id]}(e)}let g=0;class E{constructor(e){this._parent=null,this._componentCache={},this.children=[],this.inScene=!1,this.components=[],this.destroyed=!1,this._name=e,this.id=++g}get parent(){return this._parent}get name(){return this._name}set scene(e){if(this._scene===e)return;const t=this._scene;this._scene=e;for(const t of this.children)t.scene=e;e?e.addGameObject(this):t&&t.removeGameObject(this)}get scene(){return this._scene}addChild(e){if(e&&e!==this){if(!(e instanceof E))throw new Error("addChild only receive GameObject");if(this.destroyed)throw new Error(`gameObject '${this.name}' has been destroy`);if(e.parent===this){const t=this.children.indexOf(e);return t>-1&&this.children.splice(t,1),void this.children.push(e)}e.parent&&e.parent.removeChild(e),e._parent=this,this.children.push(e),e.scene=this.scene,d(e,"parent",exports.OBSERVER_TYPE.CHANGE)}}removeChild(e){if(!(e instanceof E&&e.parent&&e.parent===this))return e;const t=this.children.indexOf(e);return t>-1&&this.children.splice(t,1),e._parent=null,e.scene=null,d(e,"parent",exports.OBSERVER_TYPE.CHANGE),e}addComponent(e,t){if(this.destroyed)return;const s=function(e){return e instanceof a?e.name:e instanceof Function?e.componentName:void 0}(e);if(this._componentCache[s])return;let r;if(e instanceof Function)r=new e(t);else{if(!(e instanceof a))throw new Error("addComponent recieve Component and Component Constructor");r=e}if(r.gameObject)throw new Error(`component has been added on gameObject ${r.gameObject.name}`);return r.gameObject=this,r.init&&r.init(r.__componentDefaultParams),function(e,t=e.name){for(const s in p)(p[s]||{})[t]&&c[s]?.componentObserver?.add({component:e,type:exports.OBSERVER_TYPE.ADD,componentName:t})}(r,r.name),function(e,t=e.name){if(t&&m[t]){if(!(e&&(s=e,s&&s.constructor&&"componentName"in s.constructor)))throw new Error("component param must be an instance of Component");var s;if(!e.gameObject||!e.gameObject.id)throw new Error("component should be add to a gameObject");for(const s of m[t]){const{property:r,key:o}=h(e,s.prop);y({obj:r,key:o,prop:s,component:e,componentName:t})}}}(r,r.name),this.components.push(r),this._componentCache[s]=r,r.awake&&r.awake(),r}removeComponent(e){let t;return"string"==typeof e?t=e:e instanceof a?t=e.name:e.componentName&&(t=e.componentName),this._removeComponent(t)}_removeComponent(e){const t=this.components.findIndex(({name:t})=>t===e);if(-1===t)return;const s=this.components.splice(t,1)[0];return delete this._componentCache[e],delete s.__componentDefaultParams,s.onDestroy&&s.onDestroy(),f(s,e),s.gameObject=void 0,s}getComponent(e){let t;return"string"==typeof e?t=e:e instanceof a?t=e.name:e.componentName&&(t=e.componentName),void 0!==this._componentCache[t]?this._componentCache[t]:void 0}remove(){if(this.parent)return this.parent.removeChild(this)}destroy(){if(this.destroyed)console.error("Cannot destroy gameObject that have already been destroyed.");else{Array.from(this.children).forEach(e=>{e.destroy()}),this.remove(),this.children.length=0;for(const e in this._componentCache)this._removeComponent(e);this.components.length=0,this.destroyed=!0}}}class b{constructor(){this.events=[]}add({component:e,prop:s,type:r,componentName:o,gameObject:n}){const a=n??e?.gameObject;if(r===exports.OBSERVER_TYPE.REMOVE){const t=t=>e?t.component===e:t.gameObject===a&&t.componentName===o;if(this.events.find(e=>t(e)&&e.type===exports.OBSERVER_TYPE.ADD))return void(this.events=this.events.filter(e=>!t(e)));this.events=this.events.filter(e=>!t(e))}const i=this.events.findIndex(n=>n.gameObject===a&&n.component===e&&n.componentName===o&&t.isEqual(n.prop,s)&&n.type===r);i>-1&&this.events.splice(i,1),this.events.push({gameObject:a,component:e,prop:s,type:r,componentName:o})}getChanged(){return this.events}get changed(){return this.events}clear(){const e=this.events;return this.events=[],e}}class S{constructor(e){this.started=!1,this.componentObserver=new b,this.__systemDefaultParams=e;const t=this.constructor;this.name=t.systemName}destroy(){this.componentObserver=null,this.__systemDefaultParams=null,this.onDestroy?.()}}const R=function(){let e=null;return e=Date.now?Date.now:()=>(new Date).getTime(),e}(),T={originTime:0,playbackRate:1};class O{constructor(e,t){e instanceof O&&(t=e,e={}),e=Object.assign({},T,e),t&&(this._parent=t),this._createTime=R(),this._timeMark=[{globalTime:this.globalTime,localTime:-e.originTime,entropy:-e.originTime,playbackRate:e.playbackRate,globalEntropy:0}],this._parent&&(this._timeMark[0].globalEntropy=this._parent.entropy),this._playbackRate=e.playbackRate}get globalTime(){return this.parent?this.parent.currentTime:R()-this._createTime}get parent(){return this._parent}get lastTimeMark(){return this._timeMark[this._timeMark.length-1]}markTime({time:e=this.currentTime,entropy:t=this.entropy,playbackRate:s=this.playbackRate}={}){const r={globalTime:this.globalTime,localTime:e,entropy:t,playbackRate:s,globalEntropy:this.globalEntropy};this._timeMark.push(r)}get currentTime(){const{localTime:e,globalTime:t}=this.lastTimeMark;return e+(this.globalTime-t)*this.playbackRate}set currentTime(e){this.markTime({time:e})}get globalEntropy(){return this._parent?this._parent.entropy:this.globalTime}get entropy(){const{entropy:e,globalEntropy:t}=this.lastTimeMark;return e+Math.abs((this.globalEntropy-t)*this.playbackRate)}set entropy(e){if(this.entropy>e){const t=this.seekTimeMark(e);this._timeMark.length=t+1}this.markTime({entropy:e})}fork(e){return new O(e,this)}seekGlobalTime(e){const t=this.seekTimeMark(e),s=this._timeMark[t],{entropy:r,playbackRate:o,globalTime:n}=s;return n+(e-r)/Math.abs(o)}seekLocalTime(e){const t=this.seekTimeMark(e),s=this._timeMark[t],{localTime:r,entropy:o,playbackRate:n}=s;return n>0?r+(e-o):r-(e-o)}seekTimeMark(e){const t=this._timeMark;let s=0,r=t.length-1;if(e<=t[s].entropy)return s;if(e>=t[r].entropy)return r;let o=Math.floor((s+r)/2);for(;o>s&&o<r;){if(e===t[o].entropy)return o;e<t[o].entropy?r=o:e>t[o].entropy&&(s=o),o=Math.floor((s+r)/2)}return s}get playbackRate(){return this._playbackRate}set playbackRate(e){e!==this.playbackRate&&(this.markTime({playbackRate:e}),this._playbackRate=e)}get paused(){if(0===this.playbackRate)return!0;let e=this.parent;for(;e;){if(0===e.playbackRate)return!0;e=e.parent}return!1}}const _={autoStart:!0,frameRate:60};class x{constructor(e){e=Object.assign({},_,e),this._frameCount=0,this._frameDuration=1e3/e.frameRate,this.autoStart=e.autoStart,this.frameRate=e.frameRate,this.timeline=new O({originTime:0,playbackRate:1}),this._lastFrameTime=this.timeline.currentTime,this._tickers=new Set,this._requestId=null,this._ticker=()=>{this._started&&(this._requestId=requestAnimationFrame(this._ticker),this.update())},this.autoStart&&this.start()}update(){const e=this.timeline.currentTime,t=e-this._lastFrameTime;if(t>=this._frameDuration){const s=e-t%this._frameDuration,r=s-this._lastFrameTime;this._lastFrameTime=s;const o={deltaTime:r,delta:r,time:s,currentTime:s,frameCount:++this._frameCount,fps:Math.round(1e3/r)};for(const e of this._tickers)"function"==typeof e&&e(o)}}add(e){this._tickers.add(e)}remove(e){this._tickers.delete(e)}start(){this._started||(this._started=!0,this.timeline.playbackRate=1,this._requestId=requestAnimationFrame(this._ticker))}pause(){this._started=!1,this.timeline.playbackRate=0}setPlaybackRate(e){this.timeline.playbackRate=e}}class L extends E{constructor(e){super(e),this.gameObjects=[],this.scene=this}addGameObject(e){this.gameObjects.push(e),e.inScene||(e.inScene=!0,d(e,"inScene",exports.OBSERVER_TYPE.CHANGE))}removeGameObject(e){const t=this.gameObjects.indexOf(e);-1!==t&&(e.inScene&&(e.inScene=!1,d(e,"inScene",exports.OBSERVER_TYPE.CHANGE)),this.gameObjects.splice(t,1))}destroy(){this.scene=null,super.destroy(),this.gameObjects=null,this.canvas=null}}const k="0.1.4-beta.0",v="combos-game:plugin-init-success",w="combos-game:ready",A="combos-game:set-playing",M="combos-game:state-changed";function C(e,t){if("undefined"!=typeof window&&window.parent&&window.parent!==window)try{window.parent.postMessage(e,t)}catch{}}function N(e,t="*"){C({type:v,systemName:e.systemName,engineVersion:k,...e.packageName?{packageName:e.packageName}:{},...e.packageVersion?{packageVersion:e.packageVersion}:{}},t)}function j(e,t="*"){C({type:w,engineVersion:k,...void 0===e?{}:{error:e instanceof Error?e.message:String(e)}},t)}function P(e,t="*"){C({type:M,playing:e.playing,started:e.started},t)}function X(e){if(!e||"object"!=typeof e)return null;const t=e;return t.type!==A||"boolean"!=typeof t.playing?null:t.playing}const I=["knoffice.tech","converge.ai"];function D(e,t){if(t.includes("*"))return!0;if(0===t.length)return!1;let s;try{s=new URL(e)}catch{return!1}const r=e.toLowerCase(),o=s.hostname.toLowerCase();for(const e of t){const t=e.trim();if(!t)continue;if(t.includes("://")){if(r===t.toLowerCase())return!0;continue}const s=t.toLowerCase().replace(/^\./,"");if(o===s||o.endsWith(`.${s}`))return!0}return!1}function V(e){if(!e?.length)return[...I];if(e.includes("*"))return["*"];const t=new Set,s=[];for(const r of[...I,...e]){const e=r.trim();e&&!t.has(e)&&(t.add(e),s.push(e))}return s}function G(e){if("systemName"in e){const t=e.systemName;if("string"==typeof t)return t}return"UnknownSystem"}function $(e){const t=e;return{packageName:"string"==typeof t.packageName?t.packageName:void 0,packageVersion:"string"==typeof t.packageVersion?t.packageVersion:void 0}}var B;exports.LOAD_SCENE_MODE=void 0,(B=exports.LOAD_SCENE_MODE||(exports.LOAD_SCENE_MODE={})).SINGLE="SINGLE",B.MULTI_CANVAS="MULTI_CANVAS";const U=e=>{if((e instanceof S||e instanceof a)&&!e.started){e.started=!0;try{e.start&&e.start()}catch(t){e instanceof a?console.error(`${function(e){if("componentName"in e){const t=e.componentName;if("string"==typeof t)return t}return"UnknownComponent"}(e.constructor)} start error`,t):console.error(`${G(e.constructor)} start error`,t)}}};class H extends n.default{constructor({systems:e,frameRate:t=60,autoStart:s=!0,needScene:r=!0,onSystemsBootstrapComplete:o,pluginInitNotifyTargetOrigin:n="*",allowedMessageOrigins:a}={}){if(super(),this.playing=!1,this.started=!1,this.multiScenes=[],this.systems=[],this.pluginInitNotifyTargetOrigin="*",this.allowedMessageOrigins=V(),this.onHostMessage=e=>{if(!D(e.origin,this.allowedMessageOrigins))return;const t=X(e.data);null!==t&&this.setPlaying(t)},this.pluginInitNotifyTargetOrigin=n,this.allowedMessageOrigins=V(a),this.ticker=new x({autoStart:!1,frameRate:t}),this.initTicker(),"undefined"!=typeof window&&window.addEventListener("message",this.onHostMessage),e&&e.length){const t=[...e];this.bootstrapSystemsAndMaybeStart(t,r,s,o)}else o?.(this),this.notifyReady()}setPlaying(e){e?this.started?this.resume():this.start():this.pause()}notifyReady(e){j(e,this.pluginInitNotifyTargetOrigin)}flushObservedComponents(){for(const e of this.systems)try{e.update?.()}catch(t){console.error(`${G(e.constructor)} update error`,t)}}notifyStateChanged(){P({playing:this.playing,started:this.started},this.pluginInitNotifyTargetOrigin)}async bootstrapSystemsAndMaybeStart(e,t,s,r){let o;try{for(const t of e)await this.addSystem(t);t&&this.loadScene({scene:new L("scene")})}catch(e){o=e,console.error("Game bootstrap failed",e)}try{await Promise.resolve(r?.(this,o))}catch(e){o=o??e,console.error("onSystemsBootstrapComplete failed",e)}if(!o)try{this.flushObservedComponents();for(const e of this.systems)await Promise.resolve(e.beforeReady?.())}catch(e){o=e,console.error("Game beforeReady failed",e)}!o&&s&&this.start(),this.notifyReady(o)}get scene(){return this._scene}set scene(e){this._scene=e}get gameObjects(){return(e=>{const t=e?.scene?.gameObjects||[],s=e?.multiScenes.map(({gameObjects:e})=>e);let r=[];for(const e of s)r=[...r,...e];return[...t,...r]})(this)}async addSystem(e,s){let r;if(e instanceof Function)r=new e(s);else{if(!(e instanceof S))return void console.warn("can only add System");r=e}if(!this.systems.find(e=>e.constructor===r.constructor)){r.game=this,r.init&&await Promise.resolve(r.init(r.__systemDefaultParams)),N({systemName:G(r.constructor),...$(r.constructor)},this.pluginInitNotifyTargetOrigin),function(e,t){p[t.systemName]=t.observerInfo,c[t.systemName]=e}(r,r.constructor),function(e){const s=[];e instanceof Array?s.push(...e):s.push(e);for(const e of s)for(const s in e.observerInfo){m[s]=m[s]||[];const r=m[s];for(const o of e.observerInfo[s])-1===r.findIndex(e=>t.isEqual(e,o))&&m[s].push(o)}}(r.constructor);try{r.awake&&r.awake()}catch(e){console.error(`${G(r.constructor)} awake error`,e)}return this.systems.push(r),r}console.warn(`${G(r.constructor)} System has been added`)}removeSystem(e){if(!e)return;let t=-1;"string"==typeof e?t=this.systems.findIndex(t=>t.name===e):e instanceof Function?t=this.systems.findIndex(t=>t.constructor===e):e instanceof S&&(t=this.systems.findIndex(t=>t===e)),t>-1&&(this.systems[t].destroy&&this.systems[t].destroy(),this.systems.splice(t,1))}getSystem(e){return this.systems.find(t=>"string"==typeof e?t.name===e:t instanceof e)}pause(){this.playing&&(this.playing=!1,this.ticker.pause(),this.triggerPause(),this.notifyStateChanged())}start(){this.playing||(this.playing=!0,this.started=!0,this.ticker.start(),this.notifyStateChanged())}resume(){this.playing||(this.playing=!0,this.ticker.start(),this.triggerResume(),this.notifyStateChanged())}initTicker(){this.ticker.add(e=>{this.scene&&((e,t=[])=>{for(const s of t)for(const t of s.components)try{U(t),t.update&&t.update(e)}catch(e){console.error(`gameObject: ${s.name} ${t.name} update error`,e)}for(const s of t)for(const t of s.components)try{t.lateUpdate&&t.lateUpdate(e)}catch(e){console.error(`gameObject: ${s.name} ${t.name} lateUpdate error`,e)}})(e,this.gameObjects);for(const t of this.systems)try{U(t),t.update&&t.update(e)}catch(e){console.error(`${G(t.constructor)} update error`,e)}for(const t of this.systems)try{t.lateUpdate&&t.lateUpdate(e)}catch(e){console.error(`${G(t.constructor)} lateUpdate error`,e)}})}triggerResume(){(e=>{for(const t of e)for(const e of t.components)try{e.onResume&&e.onResume()}catch(s){console.error(`gameObject: ${t.name}, ${e.name}, onResume error`,s)}})(this.gameObjects);for(const e of this.systems)try{e.onResume&&e.onResume()}catch(t){console.error(`${G(e.constructor)}, onResume error`,t)}}triggerPause(){(e=>{for(const t of e)for(const e of t.components)try{e.onPause&&e.onPause()}catch(s){console.error(`gameObject: ${t.name}, ${e.name}, onPause error`,s)}})(this.gameObjects);for(const e of this.systems)try{e.onPause&&e.onPause()}catch(t){console.error(`${G(e.constructor)}, onPause error`,t)}}destroySystems(){for(const e of[...this.systems])this.removeSystem(e);this.systems.length=0}destroy(){"undefined"!=typeof window&&window.removeEventListener("message",this.onHostMessage),this.removeAllListeners(),this.pause(),this.scene.destroy(),this.destroySystems(),this.ticker=null,this.scene=null,this.canvas=null,this.multiScenes=null}loadScene({scene:e,mode:t=exports.LOAD_SCENE_MODE.SINGLE,params:s={}}){if(e){switch(e.game=this,t){case exports.LOAD_SCENE_MODE.SINGLE:this.scene=e;break;case exports.LOAD_SCENE_MODE.MULTI_CANVAS:this.multiScenes.push(e)}this.emit("sceneChanged",{scene:e,mode:t,params:s})}}}function Y(e,t){e.constructor.IDEProps||(e.constructor.IDEProps=[]),e.constructor.IDEProps.push(t)}function F(e={}){return function(t){if(!t.observerInfo){for(const t in e)for(const s in e[t]){let r;"string"==typeof e[t][s]&&(e[t][s]=[e[t][s]]),Array.isArray(e[t][s])&&(r={prop:e[t][s],deep:!1},e[t][s]=r),r=e[t][s],"string"==typeof r.prop&&(r.prop=[r.prop])}t.observerInfo=e}}}var q;exports.LOAD_EVENT=void 0,(q=exports.LOAD_EVENT||(exports.LOAD_EVENT={})).START="start",q.PROGRESS="progress",q.LOADED="loaded",q.COMPLETE="complete",q.ERROR="error";class z extends n.default{constructor({resource:e,resourceTotal:t}){super(),this.progress=0,this.resourceTotal=0,this.resourceLoadedCount=0,this.resource=e,this.resourceTotal=t,0===t&&this.resource.emit(exports.LOAD_EVENT.COMPLETE,this)}onStart(){this.resource.emit(exports.LOAD_EVENT.START,this)}onProgress(e){this.resourceLoadedCount++,this.progress=Math.floor(this.resourceLoadedCount/this.resourceTotal*100)/100,e.success?this.resource.emit(exports.LOAD_EVENT.LOADED,this,e):this.resource.emit(exports.LOAD_EVENT.ERROR,this,e),this.resource.emit(exports.LOAD_EVENT.PROGRESS,this,e),this.resourceLoadedCount===this.resourceTotal&&this.resource.emit(exports.LOAD_EVENT.COMPLETE,this)}}const J={image:"png",json:"json",tex:"tex",ske:"ske",audio:"audio",video:"video",model:"glb",mtl:"mtl",bin:"bin",vertex:"vert",fragment:"frag"},W={png:"png",jpg:"jpg",jpeg:"jpeg",webp:"webp",json:"json",tex:"tex",ske:"ske",mp3:"audio",wav:"audio",aac:"audio",ogg:"audio",mp4:"video",webm:"video",glb:"glb",gltf:"gltf",fbx:"fbx",obj:"obj",mtl:"mtl",stl:"stl",dae:"dae",ply:"ply",bin:"bin",vert:"vert",frag:"frag",glsl:"glsl"};function K(e,t){return W[function(e){const t=e.split("?")[0].split("#")[0],s=t.lastIndexOf(".");return s<0?"":t.slice(s+1).toLowerCase()}(e)]||J[t]||"png"}function Q(e,t,s){if("string"==typeof t){const r=t.trim();if(!r)throw Z(s,e);return{type:K(r,e),url:r}}if(!t||"object"!=typeof t||Array.isArray(t))throw new Error(`Resource "${s}" src.${e} must be a url string or { type, url }.`);const r=t;if("data"===r.type)return r;const o="string"==typeof r.url?r.url.trim():"";if(!o)throw Z(s,e);return{...r,type:r.type||K(o,e),url:o}}function Z(e,t){return new Error(`Resource "${e}" src.${t}.url is required. Use { type, url } (e.g. { type: "webp", url: "https://..." }). A flat path string is also accepted and will be normalized.`)}function ee(e,t,s="(unnamed)"){if(null==e)throw new Error(`Resource "${s}" src is required.`);if("string"==typeof e){const r=function(e){return"AUDIO"===e?"audio":"VIDEO"===e?"video":"MODEL"===e||"GLB"===e?"model":"SHADER"===e?"fragment":"image"}(t);return{[r]:Q(r,e,s)}}if("object"!=typeof e||Array.isArray(e))throw new Error(`Resource "${s}" src must be a url string or a slot object.`);const r={};for(const[t,o]of Object.entries(e))null!=o&&(r[t]=Q(t,o,s));if(0===Object.keys(r).length)throw new Error(`Resource "${s}" src has no loadable slots.`);return r}const te={AbstractLoadStrategy:s.AbstractLoadStrategy,AudioLoadStrategy:s.AudioLoadStrategy,ImageLoadStrategy:s.ImageLoadStrategy,XhrResponseType:s.XhrResponseType,MediaElementLoadStrategy:s.MediaElementLoadStrategy,VideoLoadStrategy:s.VideoLoadStrategy,XhrLoadStrategy:s.XhrLoadStrategy,Loader:s.Loader,Resource:s.Resource,ResourceType:s.ResourceType,ResourceState:s.ResourceState};function se(e){const t=e.frame;return t&&"object"==typeof t?{...e,frame:t,rotated:Boolean(e.rotated),trimmed:Boolean(e.trimmed),spriteSourceSize:e.spriteSourceSize,sourceSize:e.sourceSize}:null}function re(e,t,s){for(const r of e){if(!r||"object"!=typeof r)continue;const e=r,o=String(e.filename||e.name||"");if(!o)continue;const n=se(e);if(!n)continue;t[o]=n;const a=o.lastIndexOf("/");if(a>0){const e=o.slice(0,a);s[e]||(s[e]=[]),s[e].includes(o)||s[e].push(o)}}}var oe;exports.RESOURCE_TYPE=void 0,(oe=exports.RESOURCE_TYPE||(exports.RESOURCE_TYPE={})).IMAGE="IMAGE",oe.SPRITE="SPRITE",oe.SPRITE_ANIMATION="SPRITE_ANIMATION",oe.AUDIO="AUDIO",oe.VIDEO="VIDEO",oe.GLB="GLB",oe.MODEL="MODEL",oe.SHADER="SHADER",s.XhrLoadStrategy.setExtensionXhrType("json",s.XhrResponseType.Json),s.XhrLoadStrategy.setExtensionXhrType("tex",s.XhrResponseType.Json),s.XhrLoadStrategy.setExtensionXhrType("ske",s.XhrResponseType.Json),s.XhrLoadStrategy.setExtensionXhrType("mp3",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("wav",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("aac",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("ogg",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("glb",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("gltf",s.XhrResponseType.Json),s.XhrLoadStrategy.setExtensionXhrType("fbx",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("stl",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("ply",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("bin",s.XhrResponseType.Buffer),s.XhrLoadStrategy.setExtensionXhrType("obj",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("mtl",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("dae",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("vert",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("frag",s.XhrResponseType.Text),s.XhrLoadStrategy.setExtensionXhrType("glsl",s.XhrResponseType.Text);const ne={png:s.ImageLoadStrategy,jpg:s.ImageLoadStrategy,jpeg:s.ImageLoadStrategy,webp:s.ImageLoadStrategy,json:s.XhrLoadStrategy,tex:s.XhrLoadStrategy,ske:s.XhrLoadStrategy,audio:s.XhrLoadStrategy,video:s.VideoLoadStrategy,glb:s.XhrLoadStrategy,gltf:s.XhrLoadStrategy,fbx:s.XhrLoadStrategy,obj:s.XhrLoadStrategy,mtl:s.XhrLoadStrategy,stl:s.XhrLoadStrategy,dae:s.XhrLoadStrategy,ply:s.XhrLoadStrategy,bin:s.XhrLoadStrategy,vert:s.XhrLoadStrategy,frag:s.XhrLoadStrategy,glsl:s.XhrLoadStrategy};class ae extends n.default{constructor(e){super(),this.timeout=6e3,this.preProcessResourceHandlers=[],this.resourcesMap={},this.makeInstanceFunctions={},this.destroyInstanceFunctions={},this.promiseMap={},this.loaders=[],e&&"number"==typeof e.timeout&&(this.timeout=e.timeout)}loadConfig(e){this.addResource(e),this.preload()}loadSingle(e){return this.addResource([e]),this.getResource(e.name)}addResource(e){if(!e||e.length<1)console.warn("no resources");else for(const t of e)this.resourcesMap[t.name]?console.warn(t.name+" was already added"):this.resourcesMap[t.name]={...t,src:ee(t.src,String(t.type),t.name),data:{}}}addPreProcessResourceHandler(e){this.preProcessResourceHandlers.push(e)}removePreProcessResourceHandler(e){this.preProcessResourceHandlers.splice(this.preProcessResourceHandlers.indexOf(e),1)}preload(){const e=[];for(const t in this.resourcesMap){const s=this.resourcesMap[t];!s.preload||s.complete||this.promiseMap[t]||e.push(s.name)}this.progress=new z({resource:this,resourceTotal:e.length}),this.loadResource({names:e,preload:!0})}async getResource(e){return this.loadResource({names:[e]}),this.promiseMap[e]||Promise.resolve({})}async instance(e){const t=this.resourcesMap[e];return this.makeInstanceFunctions[t.type]&&await this.makeInstanceFunctions[t.type](t)}async destroy(e){await this._destroy(e)}async _destroy(e,t=!1){const s=this.resourcesMap[e];if(s){if(!t)try{this.destroyInstanceFunctions[s.type]&&await this.destroyInstanceFunctions[s.type](s)}catch(e){console.warn(`destroy resource ${s.name} error with '${e.message}'`)}delete this.promiseMap[e],s.data={},s.complete=!1,s.instance=void 0}}registerResourceType(e,t=e){if(exports.RESOURCE_TYPE[e])throw new Error(`The type ${e} already exists in RESOURCE_TYPE`);exports.RESOURCE_TYPE[e]=t}registerInstance(e,t){this.makeInstanceFunctions[e]=t}registerDestroy(e,t){this.destroyInstanceFunctions[e]=t}loadResource({names:e=[],preload:t=!1}){const s=e.filter(e=>!this.promiseMap[e]&&this.resourcesMap[e]);if(!s.length)return;const r={},o=this.getLoader(t);s.forEach(e=>{this.promiseMap[e]=new Promise(t=>r[e]=t);const s=this.resourcesMap[e];for(const e of this.preProcessResourceHandlers)e(s);s.src=ee(s.src,String(s.type),s.name);for(const n in s.src){const a=s.src[n].type;"data"===a?(s.data[n]=s.src[n].data,this.doComplete(e,r[e],t)):o.add({url:s.src[n].url,name:`${s.name}_${n}`,strategy:ne[a],metadata:{key:n,name:s.name,resolves:r}})}}),o.load()}async doComplete(e,t,s=!1){const r=this.resourcesMap[e],o={name:e,resource:this.resourcesMap[e],success:!0};if(this.checkAllLoaded(e))try{r.instance=await this.instance(e),r.complete=!0,s&&this.progress.onProgress(o),t(r)}catch(e){console.error(e),r.complete=!1,s&&(o.errMsg=e.message,o.success=!1,this.progress.onProgress(o)),t({})}}checkAllLoaded(e){const t=this.resourcesMap[e];return Array.from(Object.keys(t.src)).every(e=>t.data[e])}getLoader(e=!1){let t=this.loaders.find(({loading:e})=>!e);return t||(t=new s.Loader,this.loaders.push(t)),e&&t.onStart.once(()=>{this.progress.onStart()}),t.onLoad.add((t,s)=>{this.onLoad({preload:e,resource:s})}),t.onError.add((t,s,r)=>{this.onError({errMsg:t,resource:r,preload:e})}),t.onComplete.once(()=>{t.onLoad.detachAll(),t.onError.detachAll(),t.reset()}),t}async onLoad({preload:e=!1,resource:t}){const{metadata:{key:s,name:r,resolves:o},data:n}=t;this.resourcesMap[r].data[s]=n,this.doComplete(r,o[r],e)}async onError({errMsg:e,preload:t=!1,resource:s}){const{metadata:{name:r,resolves:o}}=s;if(this._destroy(r,!0),o[r]({}),t){const t={name:r,resource:this.resourcesMap[r],success:!1,errMsg:e};this.progress.onProgress(t)}}}const ie=new ae;ie.registerInstance(exports.RESOURCE_TYPE.SHADER,e=>({vertex:"string"==typeof e.data?.vertex?e.data.vertex:"",fragment:"string"==typeof e.data?.fragment?e.data.fragment:""}));const ce={IDEProp:Y,componentObserver:F};console.log(`@combos-fun/engine version: ${k}`),exports.COMBOS_GAME_PLUGIN_INIT_SUCCESS=v,exports.COMBOS_GAME_READY=w,exports.COMBOS_GAME_SET_PLAYING=A,exports.COMBOS_GAME_STATE_CHANGED=M,exports.Component=a,exports.DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES=I,exports.Game=H,exports.GameObject=E,exports.IDEProp=Y,exports.RESOURCE_TYPE_STRATEGY=ne,exports.Scene=L,exports.System=S,exports.componentObserver=F,exports.decorators=ce,exports.isAllowedMessageOrigin=D,exports.mergeAllowedMessageOrigins=V,exports.normalizeResourceSrc=ee,exports.normalizeSpritesheetData=function(e){if(!e||"object"!=typeof e)return{frames:{},animations:{},meta:{scale:1}};const t=e;if(function(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}(t.frames))return{...t,frames:t.frames,animations:{...t.animations||{}},meta:{scale:1,...t.meta||{}}};const s={},r={...t.animations||{}};if(Array.isArray(t.textures)){for(const e of t.textures)e&&"object"==typeof e&&Array.isArray(e.frames)&&re(e.frames,s,r);const e=t.textures[0]||{},o=e.scale??t.meta?.scale??1;return{frames:s,animations:r,meta:{scale:"number"==typeof o||"string"==typeof o?o:1,image:"string"==typeof e.image?e.image:void 0,size:e.size,format:e.format,...t.meta||{}}}}return Array.isArray(t.frames)?(re(t.frames,s,r),{frames:s,animations:r,meta:{scale:1,...t.meta||{}}}):{frames:{},animations:r,meta:{scale:1,...t.meta||{}}}},exports.parseSetPlayingMessage=X,exports.postParentGameReady=j,exports.postParentGameState=P,exports.postParentPluginInitSuccess=N,exports.resource=ie,exports.resourceLoader=te,exports.version=k;
package/dist/engine.d.ts CHANGED
@@ -182,6 +182,13 @@ declare class System<T extends {} = {}> {
182
182
  * @override
183
183
  */
184
184
  start?(): void;
185
+ /**
186
+ * Called once after scene bootstrap (`onSystemsBootstrapComplete`) and
187
+ * observed component changes have been flushed, before `start` / `ready`.
188
+ * May return a Promise to hold the game from entering play (e.g. first i18n).
189
+ * @override
190
+ */
191
+ beforeReady?(): void | Promise<void>;
185
192
  /**
186
193
  * Called in each tick
187
194
  * @example
@@ -250,10 +257,12 @@ interface GameParams {
250
257
  allowedMessageOrigins?: string[];
251
258
  /**
252
259
  * Called after async system bootstrap finishes (constructor path with non-empty `systems`),
253
- * i.e. after all `addSystem`/`init` work and optional `loadScene` / `start`.
254
- * Second argument is set if bootstrap threw (e.g. async `init` rejection).
260
+ * i.e. after all `addSystem`/`init` work and optional `loadScene`.
261
+ * Scene objects should be added here. The game then flushes observers, awaits
262
+ * each System `beforeReady`, then optional `start`, then `combos-game:ready`.
263
+ * May return a Promise. Second argument is set if bootstrap threw.
255
264
  */
256
- onSystemsBootstrapComplete?: (game: Game, error?: unknown) => void;
265
+ onSystemsBootstrapComplete?: (game: Game, error?: unknown) => void | Promise<void>;
257
266
  }
258
267
  declare enum LOAD_SCENE_MODE {
259
268
  SINGLE = "SINGLE",
@@ -316,8 +325,10 @@ declare class Game extends EventEmitter {
316
325
  */
317
326
  setPlaying(playing: boolean): void;
318
327
  private notifyReady;
328
+ /** Drain queued component observer events without starting the ticker. */
329
+ private flushObservedComponents;
319
330
  private notifyStateChanged;
320
- /** When `systems` is passed in the constructor, run async `init` for each before `loadScene` / `start`. */
331
+ /** When `systems` is passed in the constructor, run async `init` for each before `loadScene` / `beforeReady` / `start`. */
321
332
  private bootstrapSystemsAndMaybeStart;
322
333
  /**
323
334
  * Get scene on this game
@@ -927,7 +938,7 @@ declare function isAllowedMessageOrigin(origin: string, allowed: readonly string
927
938
  declare function mergeAllowedMessageOrigins(extra?: string[]): string[];
928
939
 
929
940
  /** Generated at build from package.json */
930
- declare const version = "0.1.2";
941
+ declare const version = "0.1.4-beta.0";
931
942
 
932
943
  /** Decorators util */
933
944
  declare const decorators: {
@@ -885,7 +885,7 @@ class Scene extends GameObject {
885
885
  }
886
886
 
887
887
  /** Generated at build from package.json */
888
- const version = "0.1.2";
888
+ const version = "0.1.4-beta.0";
889
889
 
890
890
  /**
891
891
  * Sent to `window.parent` after each `System.init` completes during `Game.addSystem`
@@ -1202,10 +1202,21 @@ class Game extends EventEmitter {
1202
1202
  notifyReady(error) {
1203
1203
  postParentGameReady(error, this.pluginInitNotifyTargetOrigin);
1204
1204
  }
1205
+ /** Drain queued component observer events without starting the ticker. */
1206
+ flushObservedComponents() {
1207
+ for (const system of this.systems) {
1208
+ try {
1209
+ system.update?.();
1210
+ }
1211
+ catch (e) {
1212
+ console.error(`${systemClassName(system.constructor)} update error`, e);
1213
+ }
1214
+ }
1215
+ }
1205
1216
  notifyStateChanged() {
1206
1217
  postParentGameState({ playing: this.playing, started: this.started }, this.pluginInitNotifyTargetOrigin);
1207
1218
  }
1208
- /** When `systems` is passed in the constructor, run async `init` for each before `loadScene` / `start`. */
1219
+ /** When `systems` is passed in the constructor, run async `init` for each before `loadScene` / `beforeReady` / `start`. */
1209
1220
  async bootstrapSystemsAndMaybeStart(systemsToAdd, needScene, autoStart, onComplete) {
1210
1221
  let error;
1211
1222
  try {
@@ -1215,18 +1226,34 @@ class Game extends EventEmitter {
1215
1226
  if (needScene) {
1216
1227
  this.loadScene({ scene: new Scene("scene") });
1217
1228
  }
1218
- if (autoStart) {
1219
- this.start();
1220
- }
1221
1229
  }
1222
1230
  catch (e) {
1223
1231
  error = e;
1224
1232
  console.error("Game bootstrap failed", e);
1225
1233
  }
1226
- finally {
1227
- onComplete?.(this, error);
1228
- this.notifyReady(error);
1234
+ try {
1235
+ await Promise.resolve(onComplete?.(this, error));
1236
+ }
1237
+ catch (e) {
1238
+ error = error ?? e;
1239
+ console.error("onSystemsBootstrapComplete failed", e);
1240
+ }
1241
+ if (!error) {
1242
+ try {
1243
+ this.flushObservedComponents();
1244
+ for (const system of this.systems) {
1245
+ await Promise.resolve(system.beforeReady?.());
1246
+ }
1247
+ }
1248
+ catch (e) {
1249
+ error = e;
1250
+ console.error("Game beforeReady failed", e);
1251
+ }
1252
+ }
1253
+ if (!error && autoStart) {
1254
+ this.start();
1229
1255
  }
1256
+ this.notifyReady(error);
1230
1257
  }
1231
1258
  /**
1232
1259
  * Get scene on this game