@utsp/runtime-client 0.15.0-nightly.20251227200128.cdc09ed → 0.15.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/dist/index.cjs +1 -1
- package/dist/index.d.ts +120 -20
- package/dist/index.mjs +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var E=Object.defineProperty;var pe=Object.getOwnPropertyDescriptor;var ge=Object.getOwnPropertyNames;var me=Object.prototype.hasOwnProperty;var fe=(y,e,t)=>e in y?E(y,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):y[e]=t;var b=(y,e)=>E(y,"name",{value:e,configurable:!0});var ye=(y,e)=>{for(var t in e)E(y,t,{get:e[t],enumerable:!0})},be=(y,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of ge(e))!me.call(y,r)&&r!==t&&E(y,r,{get:()=>e[r],enumerable:!(i=pe(e,r))||i.enumerable});return y};var ve=y=>be(E({},"__esModule",{value:!0}),y);var a=(y,e,t)=>(fe(y,typeof e!="symbol"?e+"":e,t),t);var Ie={};ye(Ie,{AudioFeature:()=>z,AudioManager:()=>ue.AudioManager,BaseClientRuntime:()=>P,ClientRuntime:()=>V,InputFeature:()=>G,PostProcessFeature:()=>q,RendererType:()=>W,ScalingMode:()=>he.ScalingMode});module.exports=ve(Ie);var ne=require("@utsp/core"),$=require("@utsp/render");var W=(t=>(t.TerminalGL="webgl",t.Terminal2D="terminal2d",t))(W||{});var H=class H{constructor(e=60){a(this,"frameCount",0);a(this,"fps",0);a(this,"fpsUpdateTime",0);a(this,"lastCoreTime",0);a(this,"lastRenderTime",0);a(this,"lastTotalTime",0);a(this,"coreTimeSamples",[]);a(this,"renderTimeSamples",[]);a(this,"totalTimeSamples",[]);a(this,"maxSamples");if(e<=0)throw new Error("maxSamples must be positive");this.maxSamples=e}startTracking(e){this.fpsUpdateTime=e,this.frameCount=0,this.fps=0}updateFPS(e){this.frameCount++,e-this.fpsUpdateTime>=1e3&&(this.fps=Math.round(this.frameCount*1e3/(e-this.fpsUpdateTime)),this.frameCount=0,this.fpsUpdateTime=e)}recordFrameTiming(e,t){let i=e+t;this.lastCoreTime=e,this.lastRenderTime=t,this.lastTotalTime=i,this.coreTimeSamples.push(e),this.renderTimeSamples.push(t),this.totalTimeSamples.push(i),this.coreTimeSamples.length>this.maxSamples&&(this.coreTimeSamples.shift(),this.renderTimeSamples.shift(),this.totalTimeSamples.shift())}getFPS(){return this.fps}getLastFrameTiming(){if(!(this.lastCoreTime===0&&this.lastRenderTime===0))return{coreTime:this.lastCoreTime,renderTime:this.lastRenderTime,totalTime:this.lastTotalTime}}getAverageFrameTiming(){if(this.coreTimeSamples.length!==0)return{coreTime:this.average(this.coreTimeSamples),renderTime:this.average(this.renderTimeSamples),totalTime:this.average(this.totalTimeSamples)}}getStats(){return{fps:this.fps,lastFrame:this.getLastFrameTiming(),avgFrame:this.getAverageFrameTiming(),sampleCount:this.coreTimeSamples.length,maxSamples:this.maxSamples}}reset(){this.frameCount=0,this.fps=0,this.fpsUpdateTime=0,this.lastCoreTime=0,this.lastRenderTime=0,this.lastTotalTime=0,this.coreTimeSamples=[],this.renderTimeSamples=[],this.totalTimeSamples=[]}average(e){return e.length===0?0:e.reduce((t,i)=>t+i,0)/e.length}};b(H,"PerformanceMonitor");var O=H;var Ce="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",A={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function re(){let y=atob(Ce),e=new Uint8Array(y.length);for(let t=0;t<y.length;t++)e[t]=y.charCodeAt(t);return e}b(re,"decodeDefaultAtlas");var Q=class Q{constructor(e,t={}){a(this,"renderer");a(this,"options");this.renderer=e,this.options={debug:t.debug??!1,useImageDataRendering:t.useImageDataRendering??!1}}async initialize(e){if("setImageDataRendering"in this.renderer&&this.options.useImageDataRendering&&(this.renderer.setImageDataRendering(!0),this.log("ImageData rendering enabled")),"setImageFont"in this.renderer&&typeof this.renderer.setImageFont=="function"){this.log("Loading default 8x8 font atlas...");let t=re();await this.renderer.setImageFont(t,A.glyphWidth,A.glyphHeight,A.cellWidth,A.cellHeight,A.atlasBlocks),this.log("Default 8x8 font atlas loaded")}await this.waitForReady(),this.log("Initialized and ready")}async waitForReady(e=100,t=50){this.log("Waiting for renderer to be ready");let i=0;return new Promise((r,n)=>{let o=b(()=>{if(i++,this.renderer.isReady())this.log(`Renderer ready after ${i*t}ms`),r();else if(i>=e){let d=`Renderer failed to be ready after ${e*t}ms`;this.log(d),n(new Error(d))}else setTimeout(o,t)},"check");o()})}async loadDefaultFont(e){console.warn("[RendererManager] loadDefaultFont() is deprecated. Use ImageFont instead.")}render(e,t){if(!this.renderer.isReady())return console.warn("[RENDERER MANAGER] Renderer not ready"),!1;let i=e.getRenderState(t);if(!i||i.displays.length===0)return console.warn("[RENDERER MANAGER] No render state or no displays"),!1;let r=i.displays[0];return this.renderer.renderDisplayData(r),!0}getRenderer(){return this.renderer}getCanvas(){return this.renderer.getCanvas()}getAvailableSize(){if(this.renderer.getAvailableSize)return this.renderer.getAvailableSize();let e=this.getCanvas();return{width:e?.clientWidth||e?.width||0,height:e?.clientHeight||e?.height||0}}isReady(){return this.renderer.isReady()}destroy(){this.log("Destroying renderer"),this.renderer.destroy()}log(e){this.options.debug&&console.warn(`[RendererManager] ${e}`)}};b(Q,"RendererManager");var L=Q;var K=class K{constructor(e){a(this,"core");a(this,"rendererManager");a(this,"rendererType");a(this,"options");a(this,"running",!1);a(this,"startTime",0);a(this,"lastTimestamp",0);a(this,"userId","");a(this,"visibilityChangeHandler");a(this,"tickRate",30);a(this,"accumulatedTime",0);a(this,"FRAME_TIME_MIN",1e3/60);a(this,"lastRenderTimestamp",0);a(this,"rafId",0);a(this,"firstTickDone",!1);a(this,"performanceMonitor");a(this,"renderMode","continuous");a(this,"renderRequested",!1);this.options={application:e.application,container:e.container,debug:e.debug??!1,width:e.width??80,height:e.height??25,renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,renderMode:e.renderMode??"continuous"},this.log("Initializing BaseClientRuntime"),this.core=new ne.Core({mode:"client",maxUsers:1}),this.core.onPaletteChanged(i=>{this.onCorePaletteChanged(i)}),this.core.onBitmapFontChanged(i=>{this.onCoreBitmapFontChanged(i)}),this.core.onImageFontChanged(i=>{this.onCoreImageFontChanged(i)}),this.visibilityChangeHandler=()=>{!document.hidden&&this.running&&(this.lastTimestamp=performance.now(),this.accumulatedTime=0,this.log("Tab visible: Reset timing"))},document.addEventListener("visibilitychange",this.visibilityChangeHandler);let t=this.createRenderer();this.rendererType=this.options.renderer,this.rendererManager=new L(t,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering}),this.performanceMonitor=new O(60),this.tickRate=20,this.renderMode=e.renderMode??"continuous",this.log(`Configured: tickRate=${this.tickRate}, renderMode=${this.renderMode}`),this.log("BaseClientRuntime initialized")}createRenderer(){if((this.options.renderer??"webgl")==="terminal2d"){this.log("Creating Terminal 2D renderer");let n=new $.Terminal2D(this.options.container,{fixedCols:this.options.width,fixedRows:this.options.height,cellAspectRatio:1});return this.options.useImageDataRendering&&(this.log("Enabling ImageData rendering (pixel-perfect, optimized)"),n.setImageDataRendering(!0)),this.log("Canvas 2D renderer created successfully"),n}this.log("Creating TerminalGL renderer");let i=document.createElement("canvas").getContext("webgl");if(!i)throw new Error("WebGL not supported. TerminalGL requires WebGL 1.0.");if(!i.getExtension("OES_element_index_uint"))throw new Error("OES_element_index_uint extension not supported. TerminalGL requires this extension for large terminals (256x256).");if(!(this.options.container instanceof HTMLDivElement))throw new Error(`TerminalGL requires container to be an HTMLDivElement. Received: ${this.options.container.tagName}`);let r=new $.TerminalGL(this.options.container,{cols:this.options.width,rows:this.options.height,charWidth:8,charHeight:8});return this.log("TerminalGL created successfully"),r}getRendererType(){return this.rendererType}isRunning(){return this.running}setTickRate(e){if(e<0||e>1e3)throw new Error(`Invalid tick rate: ${e}. Must be between 0 and 1000.`);if(this.tickRate=e,this.userId){let t=this.core.getUser(this.userId);t&&t.setBytesTickRate(e>0?e:20)}e===0&&this.renderMode==="continuous"?(this.renderMode="on-demand",this.log("Tick rate set to 0, switched to on-demand render mode")):this.log(`Tick rate set to ${e} TPS`)}setRenderMode(e){this.renderMode=e,this.log(`Render mode set to ${e}`)}setMaxFPS(e){if(e<=0||e>240)throw new Error(`Invalid FPS: ${e}. Must be between 1 and 240.`);this.FRAME_TIME_MIN=1e3/e,this.log(`Max FPS set to ${e}`)}getTickRate(){return this.tickRate}requestRender(){this.renderMode==="on-demand"&&(this.renderRequested=!0,this.log("Render requested"))}async start(){if(this.running){this.log("Already running");return}this.log("Starting BaseClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.options.application&&(this.log("Calling application.init()"),await this.options.application.init(this.core,this)),await this.onAfterInit(),await this.createLocalUser(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onBeforeStart(){}async onAfterInit(){}async stop(){if(!this.running)return;this.log("Stopping BaseClientRuntime"),this.running=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),await this.onStop();let e=this.core.getUser(this.userId);e&&this.options.application?.destroyUser&&this.options.application.destroyUser(this.core,e,"Runtime stopped"),this.log("BaseClientRuntime stopped")}async onStop(){}getStats(){let e=this.performanceMonitor.getStats();return{running:this.running,userCount:this.core.getUsers().length,fps:e.fps,uptime:this.running?performance.now()-this.startTime:0,totalFrames:0,lastFrameTiming:e.lastFrame,avgFrameTiming:e.avgFrame}}getTotalBytesReceived(){return this.core.getUser(this.userId)?.getTotalBytesReceived()??0}getBytesReceivedPerSecond(){return this.core.getUser(this.userId)?.getBytesReceivedPerSecond()??0}resetBytesReceived(){let e=this.core.getUser(this.userId);e&&e.resetByteCounters()}async destroy(){await this.stop(),this.log("Destroying BaseClientRuntime"),this.visibilityChangeHandler&&(document.removeEventListener("visibilitychange",this.visibilityChangeHandler),this.visibilityChangeHandler=void 0),await this.onDestroy();let e=this.rendererManager.getRenderer();e&&"clearOnResizeCallback"in e&&e.clearOnResizeCallback(),this.options.application?.destroy&&this.options.application.destroy(),this.rendererManager.destroy(),this.log("BaseClientRuntime destroyed")}async onDestroy(){}async createLocalUser(){this.userId="local",this.log(`Creating local user: ${this.userId}`);let e=this.core.createUser(this.userId,"User");e.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.onUserCreated(e),this.options.application?.initUser&&(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,e,{username:"User"})),this.processInitialOrders(e);let t=this.core.generateAllLoadPackets();t.forEach(o=>{e.recordBytesReceived(o.length)});let i=this.core.generateMacroLoadPackets(this.userId);i.forEach(o=>{e.recordBytesReceived(o.length)});let r=e.getInputBindingsLoadPacket();r&&e.recordBytesReceived(r.length),this.log(`Simulated ${t.length} load packets, ${i.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach(({static:o,dynamic:s},d)=>{let l=this.core.getUser(d);l&&(o&&l.recordBytesReceived(o.length),s&&l.recordBytesReceived(s.length))}),this.render(),this.log("Initial render complete")}onUserCreated(e){}processInitialOrders(e){if(e.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let t=e.flushMacroOrders();e.applyMacroOrders(t)}}mainLoop(e){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(e),this.render()),this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}let t=e-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&t<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.lastRenderTimestamp=e;let i=(e-this.lastTimestamp)/1e3,r=Math.min(i,.1);this.lastTimestamp=e,this.performanceMonitor.updateFPS(e);let n=this.core.getUser(this.userId);if(!n){this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}if(this.tickRate===0){let l=performance.now();this.render();let c=performance.now()-l;this.performanceMonitor.recordFrameTiming(0,c),this.rafId=requestAnimationFrame(p=>this.mainLoop(p));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=e,this.accumulatedTime=0,this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.accumulatedTime+=r;let o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let s=0,d=0;for(;this.accumulatedTime>=o&&s<5;){let l=performance.now();this.collectAndApplyInput(n),this.options.application&&(this.options.application.update?.(this.core,o),this.options.application.updateUser?.(this.core,n,o)),this.processTickOrders(n);let c=n.updateMacros();n.processMacroEvents(c),this.core.endTick().forEach(({static:u,dynamic:h})=>{let m=(u?.length??0)+(h?.length??0);n.recordBytesReceived(m)}),d+=performance.now()-l,this.accumulatedTime-=o,s++}if(s>0){let l=performance.now();this.render();let c=performance.now()-l;this.performanceMonitor.recordFrameTiming(d,c)}this.rafId=requestAnimationFrame(l=>this.mainLoop(l))}collectAndApplyInput(e){}processTickOrders(e){if(e.clearTextInputs(),e.hasPendingMacroOrders()){let t=e.flushMacroOrders();e.applyMacroOrders(t)}}render(){let e=this.core.getUser(this.userId);if(e){let t=e.getMacroRenderOrders();for(let[i,r]of t){let n=e.getLayerById(i);n&&r.length>0&&n.addTemporaryOrders(r)}}this.rendererManager.render(this.core,this.userId)}onCorePaletteChanged(e){this.log(`Palette changed, updating renderer (${e.size} colors)`);let t=[];for(let r=0;r<256;r++){let n=e.get(r);n?t.push({r:n.r,g:n.g,b:n.b,a:n.a}):t.push({r:0,g:0,b:0,a:0})}let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update palette: renderer is null");return}"setPalette"in i&&typeof i.setPalette=="function"&&(i.setPalette(t),this.log("\u2713 Renderer palette updated successfully"))}onCoreBitmapFontChanged(e){this.log(`Bitmap font ${e} changed, updating renderer`);let t=this.core.getBitmapFont(e);if(!t){console.warn(`[BaseClientRuntime] Font ${e} not found in registry`);return}let i=new Map,r=0;for(let o=0;o<256;o++){let s=t.getGlyph(o);s&&(i.set(o,s),r++)}this.log(`Built bitmap font map with ${r} glyphs, dimensions: ${t.getCharWidth()}x${t.getCharHeight()}`);let n=this.rendererManager.getRenderer();if(!n){console.warn("[BaseClientRuntime] Cannot update font: renderer is null");return}"setBitmapFont"in n&&typeof n.setBitmapFont=="function"&&(n.setBitmapFont(i,t.getCharWidth(),t.getCharHeight(),t.getCellWidth(),t.getCellHeight()),this.log("\u2713 Renderer bitmap font updated successfully"),this.onFontChanged(n))}onCoreImageFontChanged(e){this.log(`Image font ${e} changed, updating renderer`);let t=this.core.getImageFont(e);if(!t){console.warn(`[BaseClientRuntime] Image font ${e} not found in registry`);return}this.log(`Loading image font with ${t.getAtlasBlocks()} blocks, glyph: ${t.getGlyphWidth()}x${t.getGlyphHeight()}`);let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update image font: renderer is null");return}"setImageFont"in i&&typeof i.setImageFont=="function"&&(i.setImageFont(t.getImageData(),t.getGlyphWidth(),t.getGlyphHeight(),t.getCellWidth(),t.getCellHeight(),t.getAtlasBlocks()),this.log("\u2713 Renderer image font updated successfully"),this.onFontChanged(i))}onFontChanged(e){}log(e){this.options.debug&&console.warn(`[BaseClientRuntime] ${e}`)}getCore(){return this.core}getRendererManager(){return this.rendererManager}getUserId(){return this.userId}};b(K,"BaseClientRuntime");var P=K;var S=require("@utsp/core"),x=require("@utsp/render"),F=require("@utsp/types"),T=require("@utsp/input"),se=require("@utsp/network-client"),ae=require("@utsp/audio");var oe=require("@utsp/core"),U=require("@utsp/input");var Y=class Y{constructor(e,t,i,r,n){a(this,"network");a(this,"core");a(this,"rendererManager");a(this,"performanceMonitor");a(this,"options");a(this,"audioManager",null);a(this,"postProcessCallback",null);a(this,"userId","");a(this,"lastReceivedTick",-1);a(this,"connected",!1);a(this,"totalBytesReceived",0);a(this,"lastSentViewports",new Map);a(this,"lastCollectedTouches",[]);a(this,"touchZonesCallback",null);a(this,"audioLoadingState",{totalExpected:0,loadedSounds:new Set,errors:new Map});this.network=e,this.core=t,this.rendererManager=i,this.performanceMonitor=r,this.options={serverUrl:n.serverUrl,username:n.username,token:n.token??"",debug:n.debug??!1,autoReconnect:n.autoReconnect??!0,canvasWidth:n.canvasWidth,canvasHeight:n.canvasHeight}}async connect(){this.log(`Connecting to ${this.options.serverUrl}`),await this.network.connect(),this.connected=!0,this.log("Connected to server"),this.setupNetworkHandlers()}async joinGame(e){return new Promise((t,i)=>{this.network.send("join",{username:this.options.username,token:this.options.token}),this.network.on("join_response",r=>{if(r.success){this.userId=r.userId,this.log(`Joined game as ${this.userId}`);let n=this.core.createUser(this.userId,this.options.username);n.setBytesTickRate(20),this.audioManager&&n.setAudioProcessor(this.audioManager),e?.initUser&&e.initUser(this.core,n,{username:this.options.username,token:this.options.token}),t(this.userId)}else i(new Error(r.error||"Failed to join game"))}),setTimeout(()=>i(new Error("Join timeout")),5e3)})}sendInput(e){let t=this.core.getUser(this.userId);if(!t)return;let i=this.rendererManager.getCanvas();if(i){let f=t.getDisplays(),C=f.length>0?f[0].getSize():{x:this.options.canvasWidth,y:this.options.canvasHeight},X=C.x,_=C.y,ee=this.rendererManager.getRenderer()?.getOffsets?.(),te={offsetX:ee?.offsetX??0,offsetY:ee?.offsetY??0},k=U.InputCollector.collectMousePosition(e,i,X,_,te);t.setMousePosition(k.x,k.y,k.over),t.updateMacroMouse(k.x,k.y,k.isLeftDown);let ie=U.InputCollector.collectTouchPositions(e,i,X,_,10,te);ie.forEach(B=>{t.setTouchPosition(B.id,B.x,B.y,B.over)}),this.lastCollectedTouches=ie}else this.lastCollectedTouches=[];let r=t.getInputBindingRegistry(),n=r.getAllAxes(),o=r.getAllButtons(),s=U.InputCollector.collectAxisSources(n,e),d=U.InputCollector.collectButtonSources(o,e),l=U.InputCollector.collectTextInputs(e);l.length>0&&t.setTextInputs(l);let c=n.sort((f,C)=>f.bindingId-C.bindingId),p=new Map;for(let f of c){let C=r.evaluateAxis(f.bindingId,s);p.set(f.bindingId,C),t.setAxis(f.name,C)}let u=o.sort((f,C)=>f.bindingId-C.bindingId),h=new Map;for(let f of u){let C=r.evaluateButton(f.bindingId,d);h.set(f.bindingId,C),t.setButton(f.name,C)}let m=t.getMouseDisplayInfo(),g=t.getIsMouseOnADisplay(),v=this.collectChangedViewports(),I=this.lastCollectedTouches.map(f=>({id:f.id,x:f.x,y:f.y,over:f.over})),R=(0,oe.encodeCompressedInput)(0n,p,h,m?.displayId??0,m?.localX??0,m?.localY??0,g,l,[],v,I);this.network.send("input",R)}collectChangedViewports(){let e=[],{width:t,height:i}=this.rendererManager.getAvailableSize();if(t===0||i===0)return e;let r=0,n=this.lastSentViewports.get(r);return(!n||n.pixelWidth!==t||n.pixelHeight!==i)&&(e.push({displayId:r,pixelWidth:t,pixelHeight:i}),this.lastSentViewports.set(r,{pixelWidth:t,pixelHeight:i}),this.log(`Viewport changed: display ${r} = ${t}x${i}px`)),e}disconnect(){this.connected&&(this.network.send("leave",{}),this.network.disconnect(),this.connected=!1,this.log("Disconnected from server"))}getUserId(){return this.userId}isConnected(){return this.connected}getTotalBytesReceived(){return this.totalBytesReceived}getAudioLoadingState(){let e=this.audioLoadingState.loadedSounds.size,t=this.audioLoadingState.totalExpected,i=this.audioLoadingState.errors.size,r=t>0&&e>=t,n=Array.from(this.audioLoadingState.errors.keys());return{loadedCount:e,totalExpected:t,errorCount:i,isComplete:r,errors:n}}destroy(){this.disconnect(),this.network.destroy(),this.log("NetworkSync destroyed")}setupNetworkHandlers(){this.network.on("update",e=>{try{let t=this.convertToUint8Array(e,"update");if(!t)return;this.recordBytesReceived(t.length);let i=this.core.applyUpdatePacketBuffer(this.userId,t);i?(this.postProcessCallback&&i.postProcessOrders&&i.postProcessOrders.length>0&&this.postProcessCallback(i.postProcessOrders),this.rendererManager.render(this.core,this.userId)):this.log("Failed to apply update packet")}catch(t){this.log(`Error applying update packet: ${t}`)}}),this.network.on("update-static",e=>{this.handleUpdatePacket(e,"update-static")}),this.network.on("update-dynamic",e=>{this.handleUpdatePacket(e,"update-dynamic")}),this.network.on("load",e=>{try{let t=this.convertToUint8Array(e,"load");if(!t)return;this.recordBytesReceived(t.length),this.core.applyLoadPacket(t)?this.log("Load packet applied successfully"):this.log("Failed to apply load packet")}catch(t){this.log(`Error applying load packet: ${t}`)}}),this.network.on("input-bindings",e=>{if(this.recordBytesReceived(e.length),this.core.applyInputBindingsLoadPacket(this.userId,e)){this.log("Input bindings configured");try{let i=JSON.parse(e);if(this.log(`Touch zones in packet: ${i.touchZones?.length??0}, callback defined: ${!!this.touchZonesCallback}`),i.touchZones&&i.touchZones.length>0&&this.touchZonesCallback){let r=i.touchZones.map(n=>({id:n.zoneId,x:n.x,y:n.y,width:n.width,height:n.height}));this.touchZonesCallback(r),this.log(`Touch zones configured: ${r.length} zones`)}}catch{}}else this.log("Failed to apply input bindings")}),this.audioManager&&this.setupSoundHandlers(),this.network.on("disconnect",()=>{this.connected=!1,this.log("Disconnected from server")})}handleUpdatePacket(e,t){try{let i=this.convertToUint8Array(e,t);if(!i)return;this.recordBytesReceived(i.length);let r=new DataView(i.buffer,i.byteOffset,i.byteLength),n=Number(r.getBigUint64(0,!1));if(t==="update-dynamic"&&n<this.lastReceivedTick)return;n>this.lastReceivedTick&&(this.lastReceivedTick=n);let o=this.core.applyUpdatePacketBuffer(this.userId,i);if(o){this.postProcessCallback&&o.postProcessOrders&&o.postProcessOrders.length>0&&this.postProcessCallback(o.postProcessOrders),this.options.debug&&this.debugRenderState();let s=performance.now();this.rendererManager.render(this.core,this.userId);let d=performance.now()-s;this.performanceMonitor.recordFrameTiming(0,d),this.options.debug&&console.warn(`[CLIENT] render() completed for ${t} (${d.toFixed(2)}ms)`)}else this.options.debug&&this.log(`Failed to apply ${t} packet (tick ${n})`)}catch(i){this.log(`Error applying ${t} update packet: ${i}`),console.error(i)}}convertToUint8Array(e,t){return e instanceof Uint8Array?e:e instanceof ArrayBuffer?new Uint8Array(e):Array.isArray(e)?new Uint8Array(e):e.data&&Array.isArray(e.data)?new Uint8Array(e.data):typeof e=="object"&&e.type==="Buffer"?new Uint8Array(e.data):(this.log(`Unknown data type for ${t} packet: ${typeof e}`),null)}debugRenderState(){let e=this.core.getUser(this.userId);if(e){let i=e.getLayers();console.warn(`[CLIENT] User has ${i.length} layers`),i.forEach((r,n)=>{let o=r.getOrders(),s=r.getStatic();(o.length>0||s)&&console.warn(` Layer ${n}: ${o.length} orders, static=${s}, z=${r.getZOrder()}`)})}let t=this.rendererManager.isReady();console.warn(`[CLIENT] Renderer ready: ${t}`)}log(e){this.options.debug&&console.warn(`[NetworkSync] ${e}`)}recordBytesReceived(e){this.totalBytesReceived+=e;let t=this.core.getUser(this.userId);t&&t.recordBytesReceived(e)}getSoundPacketSize(e){return e.mode==="file"?e.sounds.reduce((i,r)=>i+this.getSoundDataLength(r.data)+r.name.length+10,0):e.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,0)}getSoundDataLength(e){if(e instanceof Uint8Array)return e.length;if(e instanceof ArrayBuffer||ArrayBuffer.isView(e))return e.byteLength;let t=globalThis.Buffer;return t&&e instanceof t?e.length:e&&typeof e=="object"&&Array.isArray(e.data)?e.data.length:Array.isArray(e)?e.length:0}setAudioManager(e){if(this.audioManager=e,e&&this.userId){let t=this.core.getUser(this.userId);t&&t.setAudioProcessor(e)}this.connected&&e&&this.setupSoundHandlers()}setPostProcessCallback(e){this.postProcessCallback=e}setTouchZonesCallback(e){this.touchZonesCallback=e}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async e=>{this.recordBytesReceived(this.getSoundPacketSize(e)),e.totalSounds!==void 0&&e.totalSounds>this.audioLoadingState.totalExpected&&(this.audioLoadingState.totalExpected=e.totalSounds,this.log(`Audio loading: expecting ${e.totalSounds} total sounds`)),e.mode==="file"?await this.handleSoundLoad(e):e.mode==="external"&&await this.handleSoundExternalLoad(e)}),this.log("Sound handlers registered"))}async handleSoundLoad(e){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let t=this.audioManager.getSoundBank();if(!t){this.log("Cannot load sounds: SoundBank not initialized");return}for(let i of e.sounds)try{let r=this.normalizeSoundData(i.data);await t.loadFromData(i.soundId,i.name,r),this.log(`Loaded sound: ${i.name} (${r.length} bytes)`),this.audioLoadingState.loadedSounds.add(i.name),this.audioLoadingState.errors.delete(i.name),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.audioLoadingState.errors.set(i.name,String(r)),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}normalizeSoundData(e){if(e instanceof Uint8Array)return e;if(e instanceof ArrayBuffer)return new Uint8Array(e);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength));let t=globalThis.Buffer;if(t&&e instanceof t)return new Uint8Array(e);if(e&&typeof e=="object"&&Array.isArray(e.data))return new Uint8Array(e.data);if(Array.isArray(e))return new Uint8Array(e);throw new Error("Invalid sound data payload")}async handleSoundExternalLoad(e){if(!this.audioManager){this.log("Cannot load external sounds: AudioManager not initialized");return}let t=this.audioManager.getSoundBank();if(!t){this.log("Cannot load external sounds: SoundBank not initialized");return}for(let i of e.sounds)try{await t.loadFromUrl(i.soundId,i.name,i.url),this.log(`Loaded external sound: ${i.name} from ${i.url}`),this.audioLoadingState.loadedSounds.add(i.name),this.audioLoadingState.errors.delete(i.name),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,r),this.audioLoadingState.errors.set(i.name,String(r)),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}sendAudioAck(e){this.network.send("audio-ack",e),this.log(`Sent audio-ack: ${e.type}`)}sendBridge(e,t){this.network.sendBridge(e,t),this.log(`Bridge sent on channel '${e}'`)}onBridge(e,t){this.network.onBridge(e,t),this.log(`Bridge handler registered for channel '${e}'`)}offBridge(e,t){this.network.offBridge(e,t),this.log(`Bridge handler removed for channel '${e}'`)}removeAllBridgeHandlers(e){this.network.removeAllBridgeHandlers(e),this.log(e?`All bridge handlers removed for '${e}'`:"All bridge handlers removed")}};b(Y,"NetworkSync");var D=Y;var j=class j extends P{constructor(t){super({application:t.application,container:t.container,debug:t.debug,width:t.width,height:t.height,renderer:t.renderer,useImageDataRendering:t.useImageDataRendering,renderMode:t.renderMode});a(this,"input",null);a(this,"networkSync",null);a(this,"mode");a(this,"localOptions",null);a(this,"autoplayOverlay",null);a(this,"autoplay",!0);a(this,"audioManager",null);a(this,"mobileVibration",null);a(this,"postProcessOverlay",null);a(this,"postProcessOrderCollector",new S.PostProcessOrderCollector);a(this,"localBridgeHandlers",new Map);a(this,"localBridgeWildcardHandlers",new Set);if(this.mode=t.mode,t.mode==="local"?(this.localOptions={mode:"local",application:t.application,container:t.container,debug:t.debug??!1,width:t.width??80,height:t.height??25,userId:t.userId??"local",username:t.username??"User",renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,mobileInputConfig:t.mobileInputConfig,touchZones:t.touchZones,captureInput:t.captureInput??!1,inputEnabled:t.inputEnabled??!0,autoplay:t.autoplay??!0,autoplayOptions:t.autoplayOptions,renderMode:t.renderMode??"continuous"},this.userId=t.userId??"local",this.autoplay=t.autoplay??!0):(this.localOptions={mode:"connected",application:t.application,container:t.container,serverUrl:t.serverUrl,username:t.username??"Player",debug:t.debug??!1,width:t.width??80,height:t.height??25,autoReconnect:t.autoReconnect??!0,token:t.token,renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,mobileInputConfig:t.mobileInputConfig,touchZones:t.touchZones,captureInput:t.captureInput??!1,inputEnabled:t.inputEnabled??!0,autoplay:t.autoplay??!0,autoplayOptions:t.autoplayOptions,renderMode:t.renderMode??"continuous"},this.autoplay=t.autoplay??!0),this.log(`Initializing ClientRuntime (${this.mode} mode)`),this.mode==="connected"&&(this.core=new S.Core({mode:"client",maxUsers:100}),this.core.onPaletteChanged(r=>{this.onCorePaletteChanged(r)}),this.core.onBitmapFontChanged(r=>{this.onCoreBitmapFontChanged(r)}),this.core.onImageFontChanged(r=>{this.onCoreImageFontChanged(r)})),t.inputEnabled??!0?this.input=new T.UnifiedInputRouter({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:t.touchZones?.targetElement??void 0,debug:t.debug,keyboardConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mouseConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mobileConfig:{preventDefault:t.mobileInputConfig?.preventDefault??!1,passive:t.mobileInputConfig?.passive??!0,maxTouches:t.mobileInputConfig?.maxTouches??10},enableTouchZones:t.touchZones?.enable??!1,touchZoneConfig:{targetElement:t.touchZones?.targetElement,gridWidth:t.touchZones?.gridWidth??t.width??0,gridHeight:t.touchZones?.gridHeight??t.height??0,rendererOffsets:t.touchZones?.rendererOffsets,zones:t.touchZones?.zones}}):this.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new se.SocketIOClient({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:t.debug});this.networkSync=new D(n,this.core,this.rendererManager,this.performanceMonitor,{serverUrl:r.serverUrl,username:r.username,token:r.token,debug:t.debug,autoReconnect:r.autoReconnect,canvasWidth:t.width??80,canvasHeight:t.height??25})}this.log("ClientRuntime initialized")}getMode(){return this.mode}setTickRate(t){if(this.mode==="connected"){this.log("setTickRate() has no effect in connected mode");return}super.setTickRate(t)}async onBeforeStart(){this.audioManager=new ae.AudioManager({debug:this.options.debug}),this.mobileVibration=new T.MobileVibration({debug:this.options.debug}),this.autoplay?(this.log("Autoplay enabled, initializing AudioManager..."),this.audioManager.initialize()):(this.log("Autoplay disabled, showing overlay..."),await new Promise(t=>{this.autoplayOverlay=new x.AutoplayOverlay(this.options.container,{...this.localOptions?.autoplayOptions,onStart:()=>{this.log("User clicked start button"),this.audioManager&&(this.audioManager.initialize(),this.audioManager.playStartSound()),this.mobileVibration&&this.mobileVibration.vibrate(50),t()}})}),this.log("Autoplay overlay dismissed, continuing startup..."))}async onAfterInit(){this.postProcessOverlay=new x.PostProcessOverlay(this.options.container),this.log("PostProcessOverlay created");let t=this.rendererManager.getCanvas();if(t&&this.input&&this.input.setMobileTarget(t),this.mode==="connected"&&this.networkSync){if(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),this.input){let r=this.input,n=this.rendererManager;this.networkSync.setTouchZonesCallback(o=>{this.log(`[onAfterInit] Touch zones callback called with ${o.length} zones`);let s=n.getCanvas();if(this.log(`[onAfterInit] Canvas available: ${!!s}`),s){let d=o.reduce((p,u)=>Math.max(p,u.x+u.width),0),l=o.reduce((p,u)=>Math.max(p,u.y+u.height),0);this.log(`[onAfterInit] Grid from zones: ${d}x${l}`);let c=r.enableTouchZonesDevice(s,d,l,o);this.log(`[onAfterInit] enableTouchZonesDevice returned: ${c}`),c&&this.log(`Touch zones configured from server: ${o.length} zones`)}})}if(await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application),this.input){let n=this.core.getUser(this.userId)?.getDisplays()??[];if(n.length>0){let o=n[0].getSize();this.input.updateTouchZoneLayout(o.x,o.y)}}let i=this.core.getUser(this.userId);i&&this.configureUserProcessors(i)}}async start(){if(this.running){this.log("Already running");return}this.log("Starting ClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.postProcessOverlay=new x.PostProcessOverlay(this.options.container),this.log("PostProcessOverlay created"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.options.application&&(this.log("Calling application.init()"),await this.options.application.init(this.core,this));let t=this.rendererManager.getCanvas();if(t&&this.input&&this.input.setMobileTarget(t),this.mode==="connected"&&this.networkSync){if(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(n=>{this.applyPostProcessOrders(n)}),this.input){let n=this.input,o=this.rendererManager;this.networkSync.setTouchZonesCallback(s=>{this.log(`[start] Touch zones callback called with ${s.length} zones`);let d=o.getCanvas();if(this.log(`[start] Canvas available: ${!!d}`),d){let l=s.reduce((u,h)=>Math.max(u,h.x+h.width),0),c=s.reduce((u,h)=>Math.max(u,h.y+h.height),0);this.log(`[start] Grid from zones: ${l}x${c}`);let p=n.enableTouchZonesDevice(d,l,c,s);this.log(`[start] enableTouchZonesDevice returned: ${p}`),p&&this.log(`Touch zones configured from server: ${s.length} zones`)}})}await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application);let r=this.core.getUser(this.userId);r&&this.configureUserProcessors(r)}else await this.createLocalUser();let i=this.core.getUser(this.userId);if(i){let r=i.getDisplays();if(r.length>0){let o=r[0].getSize(),s=o.x,d=o.y,l=this.rendererManager.getRenderer(),c,p;if(this.rendererType==="webgl"){let h=l.getGridSize();c=h.cols,p=h.rows}else{let u=l;c=u.getCols(),p=u.getRows()}(c!==s||p!==d)&&(this.log(`Adjusting renderer from ${c}\xD7${p} to match display ${s}\xD7${d}`),l.resize(s,d))}}this.input&&this.input.start(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onStop(){this.audioManager&&this.audioManager.stopAll(),this.input&&this.input.stop(),this.mode==="connected"&&this.networkSync&&this.networkSync.disconnect()}getStats(){let t=super.getStats(),r=this.core.getUser(this.userId)?.getTotalBytesReceived();return{...t,mode:this.mode,latency:void 0,totalBytesReceived:r}}getAudioLoadingState(){if(this.mode==="connected"&&this.networkSync)return this.networkSync.getAudioLoadingState();if(this.audioManager){let t=this.audioManager.getSoundBank();if(t){let i=t.getStats();return{loadedCount:i.totalSounds,totalExpected:i.totalSounds,errorCount:0,isComplete:!0,errors:[]}}}return{loadedCount:0,totalExpected:0,errorCount:0,isComplete:!0,errors:[]}}async onDestroy(){this.autoplayOverlay&&(this.autoplayOverlay.destroy(),this.autoplayOverlay=null),this.audioManager&&(this.audioManager.destroy(),this.audioManager=null),this.postProcessOverlay&&(this.postProcessOverlay.destroy(),this.postProcessOverlay=null),this.input&&this.input.destroy(),this.networkSync&&this.networkSync.destroy()}configureUserProcessors(t){if(this.audioManager&&t.setAudioProcessor(this.audioManager),this.mobileVibration&&t.setMobileVibrationProcessor(this.mobileVibration),this.input){let i=this.input.getGamepad();i&&t.setGamepadVibrationProcessor(i)}}async createLocalUser(){let t=this.localOptions;this.userId=t?.userId??"local",this.log(`Creating local user: ${this.userId}`);let i=this.core.createUser(this.userId,t?.username??"User");if(i.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.configureUserProcessors(i),this.options.application?.initUser&&(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,i,{username:t?.username??"User"})),i.hasAudioConfigCommands()&&(this.log("Applying audio config commands from initUser()"),i.applyAudioConfigCommands(i.flushAudioConfigCommands())),i.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let d=i.flushMacroOrders();i.applyMacroOrders(d)}if(i.hasPostProcessCommands()){this.log("Applying post-process commands from initUser()");let d=i.flushPostProcessCommands(),l=this.postProcessOrderCollector.convertCommands(d);this.applyPostProcessOrders(l)}i.needsSendSounds()&&(await this.loadSoundsFromRegistry(),i.clearSendSounds());let r=this.core.generateAllLoadPackets();r.forEach(d=>{i.recordBytesReceived(d.length)});let n=this.core.generateMacroLoadPackets(this.userId);n.forEach(d=>{i.recordBytesReceived(d.length)});let o=i.getInputBindingsLoadPacket();if(o&&(i.recordBytesReceived(o.length),this.input)){let l=i.getInputBindingRegistry().getAllTouchZones();if(l.length>0){let c=this.rendererManager.getCanvas();if(c){let p=i.getDisplays(),u=p.length>0?p[0].getSize():{x:this.options.width??80,y:this.options.height??25},h=l.map(g=>({id:g.zoneId,x:g.x,y:g.y,width:g.width,height:g.height}));this.input.enableTouchZonesDevice(c,u.x,u.y,h)?this.log(`Touch zones synchronized from registry: ${l.length} zones`):this.log("Failed to enable touch zones - mobile input may not be initialized")}}}this.log(`Simulated ${r.length} load packets, ${n.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach(({static:d,dynamic:l},c)=>{let p=this.core.getUser(c);p&&(d&&p.recordBytesReceived(d.length),l&&p.recordBytesReceived(l.length))}),this.render(),this.log("Initial render complete")}async loadSoundsFromRegistry(){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let t=this.audioManager.getSoundBank();if(!t){this.log("Cannot load sounds: SoundBank not initialized");return}let r=this.core.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry into AudioManager...`);let n=this.core.getUser(this.userId),o=0;for(let s of r)try{if(s.loadType==="file"&&s.data){await t.loadFromData(s.soundId,s.name,s.data);let d=s.data.length+s.name.length+10;o+=d,this.log(`\u{1F50A} Loaded sound: ${s.name} (${s.data.length} bytes)`)}else if(s.loadType==="external"&&s.url){await t.loadFromUrl(s.soundId,s.name,s.url);let d=s.url.length+s.name.length+10;o+=d,this.log(`\u{1F50A} Loaded external sound: ${s.name} from ${s.url}`)}}catch(d){console.error(`[ClientRuntime] Failed to load sound "${s.name}":`,d)}n&&o>0&&(n.recordBytesReceived(o),this.log(`Simulated ${o} bytes for ${r.length} sound(s)`)),this.log(`\u2713 ${t.size} sounds loaded into AudioManager`)}mainLoop(t){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(t),this.render()),this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}let i=t-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&i<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}this.lastRenderTimestamp=t;let r=(t-this.lastTimestamp)/1e3,n=Math.min(r,.1);this.lastTimestamp=t,this.performanceMonitor.updateFPS(t);let o=this.core.getUser(this.userId);if(!o){this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}if(this.mode==="local"){if(this.tickRate===0){let c=performance.now();this.render();let p=performance.now()-c;this.performanceMonitor.recordFrameTiming(0,p),this.rafId=requestAnimationFrame(u=>this.mainLoop(u));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=t,this.accumulatedTime=0,this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.accumulatedTime+=n;let s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let d=0,l=0;for(;this.accumulatedTime>=s&&d<5;){let c=performance.now();if(this.collectAndApplyInput(o),this.options.application&&(this.options.application.update?.(this.core,s),this.options.application.updateUser?.(this.core,o,s)),o.clearTextInputs(),o.hasAudioConfigCommands()&&o.applyAudioConfigCommands(o.flushAudioConfigCommands()),o.hasSoundCommands()&&o.applyAudioCommands(o.flushSoundCommands()),o.hasMobileVibrationCommands()&&o.applyMobileVibrationCommands(o.flushMobileVibrationCommands()),o.hasGamepadVibrationCommands()&&o.applyGamepadVibrationCommands(o.flushGamepadVibrationCommands()),o.hasPostProcessCommands()){let h=o.flushPostProcessCommands(),m=this.postProcessOrderCollector.convertCommands(h);this.applyPostProcessOrders(m)}if(o.hasPendingMacroOrders()){let h=o.flushMacroOrders();o.applyMacroOrders(h)}let p=o.updateMacros();if(o.processMacroEvents(p),o.hasBridgeMessages()){let h=o.getBridgeMessages();this.dispatchLocalBridgeMessages(h)}this.core.endTick().forEach(({static:h,dynamic:m})=>{let g=(h?.length??0)+(m?.length??0);o.recordBytesReceived(g)}),l+=performance.now()-c,this.accumulatedTime-=s,d++}if(d>0){let c=performance.now();this.render();let p=performance.now()-c;this.performanceMonitor.recordFrameTiming(l,p)}}else{let s=o.updateMacros();o.processMacroEvents(s),this.collectAndSendInput()}this.rafId=requestAnimationFrame(s=>this.mainLoop(s))}collectAndApplyInput(t){if(!this.input)return;let i=this.rendererManager.getCanvas();if(i){let d=t.getDisplays(),l=d.length>0?d[0].getSize():{x:this.options.width,y:this.options.height},c=l.x,p=l.y,h=this.rendererManager.getRenderer()?.getOffsets?.(),m={offsetX:h?.offsetX??0,offsetY:h?.offsetY??0};this.input.updateTouchZoneLayout(c,p,m);let g=T.InputCollector.collectMousePosition(this.input,i,c,p,m);t.setMousePosition(g.x,g.y,g.over),t.updateMacroMouse(g.x,g.y,g.isLeftDown),T.InputCollector.collectTouchPositions(this.input,i,c,p,10,m).forEach(R=>{t.setTouchPosition(R.id,R.x,R.y,R.over)});let I=this.rendererManager.getAvailableSize();t.setDisplayViewport(0,I.width,I.height)}let r=T.InputCollector.collectTextInputs(this.input);r.length>0&&t.setTextInputs(r);let n=t.getInputBindingRegistry(),o=n.getAllAxes(),s=n.getAllButtons();if(o.length>0||s.length>0){let d=T.InputCollector.collectAxisSources(o,this.input),l=T.InputCollector.collectButtonSourcesWithTransitions(s,this.input);o.forEach(c=>{let p=n.evaluateAxis(c.bindingId,d);t.setAxis(c.name,p)}),s.forEach(c=>{let p=new Map,u=new Map,h=new Map;for(let[I,R]of l)p.set(I,R.pressed),u.set(I,R.justPressed),h.set(I,R.justReleased);let m=n.evaluateButton(c.bindingId,p),g=n.evaluateButton(c.bindingId,u),v=n.evaluateButton(c.bindingId,h);t.setButton(c.name,m),t.setButton(`${c.name}_justPressed`,g),t.setButton(`${c.name}_justReleased`,v)})}this.input.poll?.()}collectAndSendInput(){!this.networkSync||!this.input||this.networkSync.sendInput(this.input)}render(){let t=this.core.getUser(this.userId);if(t){let i=t.getDisplays();if(i.length>0){let r=i[0],n=r.getSize(),o=this.rendererManager.getRenderer();if(o){let s,d,l,c,p=null;if(this.rendererType==="webgl"){let h=o,m=h.getGridSize();s=m.cols,d=m.rows,l=h.getCellWidth(),c=h.getCellHeight(),p=h.getScalingMode()}else{let h=o;s=h.getCols(),d=h.getRows(),l=h.getCellWidth(),c=h.getCellHeight(),p=h.getScalingMode()}if(p===F.ScalingMode.Responsive){let h=this.rendererManager.getAvailableSize(),m=256*256;"getMaxCells"in o&&(m=Math.min(m,o.getMaxCells()));let g=Math.floor(h.width/l),v=Math.floor(h.height/c);if(g=Math.min(256,Math.max(1,g)),v=Math.min(256,Math.max(1,v)),g*v>m){let I=Math.sqrt(m/(g*v));g=Math.max(1,Math.floor(g*I)),v=Math.max(1,Math.floor(v*I)),this.log(`\u26A0\uFE0F Device limit: max ${m} cells, clamping to ${g}\xD7${v}`)}(n.x!==g||n.y!==v)&&(this.log(`\u{1F4D0} Resizing display from ${n.x}\xD7${n.y} to ${g}\xD7${v} (available: ${h.width}\xD7${h.height}px, cell: ${l}\xD7${c}px)`),r.setSize(new F.Vector2(g,v)))}let u=r.getSize();(s!==u.x||d!==u.y)&&(this.log(`\u{1F4D0} Resizing renderer from ${s}\xD7${d} to ${u.x}\xD7${u.y}`),o.resize(u.x,u.y))}}}super.render()}onFontChanged(t){this.postProcessOverlay&&"syncWithRenderer"in this.postProcessOverlay&&"getCellWidth"in t&&"getCellHeight"in t&&"getCurrentScale"in t&&"getGridSize"in t&&(this.postProcessOverlay.syncWithRenderer(t),this.log("\u2713 PostProcessOverlay synced with renderer dimensions"))}getAudioManager(){return this.audioManager}getAudioContext(){return this.audioManager?.getContext()??null}getPostProcessOverlay(){return this.postProcessOverlay}applyAmbientEffectConfig(t){let i=this.rendererManager?.getRenderer();i&&"setAmbientEffect"in i&&(t.enabled?i.setAmbientEffect({blur:t.blur,scale:t.scale}):i.setAmbientEffect(!1),this.log(`Ambient effect ${t.enabled?"enabled":"disabled"}`))}applyPostProcessOrders(t){if(!this.postProcessOverlay){this.log("PostProcessOverlay not initialized, skipping post-process orders");return}for(let i of t){let r=i.displayId;switch(i.type){case S.PostProcessOrderType.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.postProcessOverlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffectConfig(n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case S.PostProcessOrderType.SetScanlines:{this.postProcessOverlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case S.PostProcessOrderType.SetAmbientEffect:{this.applyAmbientEffectConfig({enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case S.PostProcessOrderType.SetScalingMode:{this.applyScalingMode(r,i.mode);break}case S.PostProcessOrderType.SetGrid:{this.applyGridConfig(r,i);break}case S.PostProcessOrderType.SwitchPalette:{this.applySwitchPalette(r,i);break}case S.PostProcessOrderType.SetCellSize:{this.applyCellSize(r,i.cellWidth,i.cellHeight);break}}}}applyScalingMode(t,i){let r=this.rendererManager.getRenderer();if(r&&"setScalingMode"in r){let n=(0,F.valueToScalingMode)(i);r.setScalingMode(n),this.log(`[Display ${t}] Scaling mode set to ${n}`)}}applyGridConfig(t,i){let r=this.rendererManager.getRenderer();if(r&&"setGrid"in r){let n=i.colorA/255;r.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${n})`,lineWidth:i.lineWidth}),this.log(`[Display ${t}] Grid ${i.enabled?"enabled":"disabled"}`)}}applySwitchPalette(t,i){let r=this.core.getPaletteFromSlot(i.slotId);if(!r){console.warn(`[ClientRuntime] Palette slot ${i.slotId} not found`);return}let n=[];for(let s=0;s<256;s++){let d=r.get(s);d?n.push({r:d.r,g:d.g,b:d.b,a:d.a}):n.push({r:0,g:0,b:0,a:0})}let o=this.rendererManager.getRenderer();o&&"setPalette"in o&&(o.setPalette(n),this.log(`[Display ${t}] Switched to palette slot ${i.slotId}`))}applyCellSize(t,i,r){let n=this.rendererManager.getRenderer();n&&"setCellSize"in n&&(n.setCellSize(i,r),this.log(`[Display ${t}] Cell size set to ${i}\xD7${r}px`))}patternFromType(t){switch(t){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}dispatchLocalBridgeMessages(t){for(let i of t){let{channel:r,data:n}=i,o=this.localBridgeHandlers.get(r);if(o)for(let s of o)try{s(n)}catch(d){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,d)}for(let s of this.localBridgeWildcardHandlers)try{s(r,n)}catch(d){console.error("[ClientRuntime] Bridge wildcard handler error:",d)}this.log(`Bridge message dispatched on channel '${r}'`)}}sendBridge(t,i){if(this.mode==="connected"&&this.networkSync)this.networkSync.sendBridge(t,i);else if(this.mode==="local"){let r=this.core.getUser(this.userId);r&&this.options.application?.onBridgeMessage&&(this.options.application.onBridgeMessage(this.core,r,t,i),this.log(`Bridge message sent to application on channel '${t}'`))}}onBridge(t,i){this.mode==="connected"&&this.networkSync?this.networkSync.onBridge(t,i):this.mode==="local"&&(t==="*"?this.localBridgeWildcardHandlers.add(i):(this.localBridgeHandlers.has(t)||this.localBridgeHandlers.set(t,new Set),this.localBridgeHandlers.get(t).add(i)),this.log(`Bridge handler registered for channel '${t}'`))}offBridge(t,i){this.mode==="connected"&&this.networkSync?this.networkSync.offBridge(t,i):this.mode==="local"&&(t==="*"?this.localBridgeWildcardHandlers.delete(i):this.localBridgeHandlers.get(t)?.delete(i))}removeAllBridgeHandlers(t){this.mode==="connected"&&this.networkSync?this.networkSync.removeAllBridgeHandlers(t):this.mode==="local"&&(t===void 0?(this.localBridgeHandlers.clear(),this.localBridgeWildcardHandlers.clear()):t==="*"?this.localBridgeWildcardHandlers.clear():this.localBridgeHandlers.delete(t))}log(t){this.options.debug&&console.warn(`[ClientRuntime/${this.mode}] ${t}`)}};b(j,"ClientRuntime");var V=j;var M=require("@utsp/input");var N=class N{constructor(e={}){a(this,"input");a(this,"mobileVibration");this.input=new M.UnifiedInputRouter({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:e.debug,keyboardConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mouseConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mobileConfig:{preventDefault:e.mobileInputConfig?.preventDefault??!1,passive:e.mobileInputConfig?.passive??!0,maxTouches:e.mobileInputConfig?.maxTouches??10}}),this.mobileVibration=new M.MobileVibration({debug:e.debug})}setMobileTarget(e){this.input.setMobileTarget(e)}start(){this.input.start()}stop(){this.input.stop()}destroy(){this.input.destroy()}getRouter(){return this.input}getGamepad(){return this.input.getGamepad()}getMobileVibration(){return this.mobileVibration}collectAndApply(e,t,i,r,n){if(t){let c=M.InputCollector.collectMousePosition(this.input,t,i,r,n);e.setMousePosition(c.x,c.y,c.over),e.updateMacroMouse(c.x,c.y,c.isLeftDown),M.InputCollector.collectTouchPositions(this.input,t,i,r,10,n).forEach(u=>{e.setTouchPosition(u.id,u.x,u.y,u.over)})}let o=M.InputCollector.collectTextInputs(this.input);o.length>0&&e.setTextInputs(o);let s=e.getInputBindingRegistry(),d=s.getAllAxes(),l=s.getAllButtons();if(d.length>0||l.length>0){let c=M.InputCollector.collectAxisSources(d,this.input),p=M.InputCollector.collectButtonSourcesWithTransitions(l,this.input);d.forEach(u=>{let h=s.evaluateAxis(u.bindingId,c);e.setAxis(u.name,h)}),l.forEach(u=>{let h=new Map,m=new Map,g=new Map;for(let[f,C]of p)h.set(f,C.pressed),m.set(f,C.justPressed),g.set(f,C.justReleased);let v=s.evaluateButton(u.bindingId,h),I=s.evaluateButton(u.bindingId,m),R=s.evaluateButton(u.bindingId,g);e.setButton(u.name,v),e.setButton(`${u.name}_justPressed`,I),e.setButton(`${u.name}_justReleased`,R)})}this.input.poll?.()}processVibrationCommands(e){e.hasMobileVibrationCommands()&&e.applyMobileVibrationCommands(e.flushMobileVibrationCommands()),e.hasGamepadVibrationCommands()&&e.applyGamepadVibrationCommands(e.flushGamepadVibrationCommands())}};b(N,"InputFeature");var G=N;var de=require("@utsp/audio");var Z=class Z{constructor(e={}){a(this,"audioManager");a(this,"debug");this.debug=e.debug??!1,this.audioManager=new de.AudioManager({debug:e.debug})}initialize(){this.audioManager.initialize()}playStartSound(){this.audioManager.playStartSound()}stopAll(){this.audioManager.stopAll()}destroy(){this.audioManager.destroy()}getManager(){return this.audioManager}getContext(){return this.audioManager.getContext()}injectIntoUser(e){e.setAudioProcessor(this.audioManager)}processAudioCommands(e){e.hasAudioConfigCommands()&&e.applyAudioConfigCommands(e.flushAudioConfigCommands()),e.hasSoundCommands()&&e.applyAudioCommands(e.flushSoundCommands())}async loadSoundsFromRegistry(e){let t=this.audioManager.getSoundBank();if(!t){this.log("Cannot load sounds: SoundBank not initialized");return}let r=e.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry...`);for(let n of r)try{n.loadType==="file"&&n.data?(await t.loadFromData(n.soundId,n.name,n.data),this.log(`Loaded sound: ${n.name} (${n.data.length} bytes)`)):n.loadType==="external"&&n.url&&(await t.loadFromUrl(n.soundId,n.name,n.url),this.log(`Loaded external sound: ${n.name} from ${n.url}`))}catch(o){console.error(`[AudioFeature] Failed to load sound "${n.name}":`,o)}this.log(`${t.size} sounds loaded`)}log(e){this.debug&&console.warn(`[AudioFeature] ${e}`)}};b(Z,"AudioFeature");var z=Z;var w=require("@utsp/core"),le=require("@utsp/render"),ce=require("@utsp/types");var J=class J{constructor(e,t={}){a(this,"overlay");a(this,"orderCollector");a(this,"debug");this.debug=t.debug??!1,this.overlay=new le.PostProcessOverlay(e),this.orderCollector=new w.PostProcessOrderCollector}destroy(){this.overlay.destroy()}getOverlay(){return this.overlay}syncWithRenderer(e){this.overlay.syncWithRenderer(e),this.log("Overlay synced with renderer dimensions")}processCommands(e,t){if(!e.hasPostProcessCommands())return;let i=e.flushPostProcessCommands(),r=this.orderCollector.convertCommands(i);this.applyOrders(r,t)}applyOrders(e,t){for(let i of e){let r=i.displayId;switch(i.type){case w.PostProcessOrderType.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.overlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffect(t,n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case w.PostProcessOrderType.SetScanlines:{this.overlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case w.PostProcessOrderType.SetAmbientEffect:{this.applyAmbientEffect(t,{enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case w.PostProcessOrderType.SetScalingMode:{this.applyScalingMode(t,r,i.mode);break}case w.PostProcessOrderType.SetGrid:{this.applyGridConfig(t,r,i);break}}}}applyAmbientEffect(e,t){e&&"setAmbientEffect"in e&&(t.enabled?e.setAmbientEffect({blur:t.blur,scale:t.scale}):e.setAmbientEffect(!1))}applyScalingMode(e,t,i){if(e&&"setScalingMode"in e){let r=(0,ce.valueToScalingMode)(i);e.setScalingMode(r),this.log(`[Display ${t}] Scaling mode set to ${r}`)}}applyGridConfig(e,t,i){if(e&&"setGrid"in e){let r=i.colorA/255;e.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${r})`,lineWidth:i.lineWidth}),this.log(`[Display ${t}] Grid ${i.enabled?"enabled":"disabled"}`)}}patternFromType(e){switch(e){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}log(e){this.debug&&console.warn(`[PostProcessFeature] ${e}`)}};b(J,"PostProcessFeature");var q=J;var he=require("@utsp/render"),ue=require("@utsp/audio");
|
|
1
|
+
"use strict";var D=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var be=Object.prototype.hasOwnProperty;var ve=(S,t,e)=>t in S?D(S,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):S[t]=e;var R=(S,t)=>D(S,"name",{value:t,configurable:!0});var Ce=(S,t)=>{for(var e in t)D(S,e,{get:t[e],enumerable:!0})},Re=(S,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ye(t))!be.call(S,r)&&r!==e&&D(S,r,{get:()=>t[r],enumerable:!(i=fe(t,r))||i.enumerable});return S};var Se=S=>Re(D({},"__esModule",{value:!0}),S);var d=(S,t,e)=>(ve(S,typeof t!="symbol"?t+"":t,e),e);var we={};Ce(we,{AudioFeature:()=>H,AudioManager:()=>me.AudioManager,BaseClientRuntime:()=>O,ClientRuntime:()=>z,DEFAULT_ATLAS_CONFIG:()=>A,InputFeature:()=>W,PostProcessFeature:()=>Q,RendererManager:()=>U,RendererType:()=>K,ScalingMode:()=>ge.ScalingMode,decodeDefaultAtlas:()=>$});module.exports=Se(we);var ae=require("@utsp/core"),V=require("@utsp/render");var K=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(K||{});var Y=class Y{constructor(t=60){d(this,"frameCount",0);d(this,"fps",0);d(this,"fpsUpdateTime",0);d(this,"lastCoreTime",0);d(this,"lastRenderTime",0);d(this,"lastTotalTime",0);d(this,"coreTimeSamples",[]);d(this,"renderTimeSamples",[]);d(this,"totalTimeSamples",[]);d(this,"maxSamples");if(t<=0)throw new Error("maxSamples must be positive");this.maxSamples=t}startTracking(t){this.fpsUpdateTime=t,this.frameCount=0,this.fps=0}updateFPS(t){this.frameCount++,t-this.fpsUpdateTime>=1e3&&(this.fps=Math.round(this.frameCount*1e3/(t-this.fpsUpdateTime)),this.frameCount=0,this.fpsUpdateTime=t)}recordFrameTiming(t,e){let i=t+e;this.lastCoreTime=t,this.lastRenderTime=e,this.lastTotalTime=i,this.coreTimeSamples.push(t),this.renderTimeSamples.push(e),this.totalTimeSamples.push(i),this.coreTimeSamples.length>this.maxSamples&&(this.coreTimeSamples.shift(),this.renderTimeSamples.shift(),this.totalTimeSamples.shift())}getFPS(){return this.fps}getLastFrameTiming(){if(!(this.lastCoreTime===0&&this.lastRenderTime===0))return{coreTime:this.lastCoreTime,renderTime:this.lastRenderTime,totalTime:this.lastTotalTime}}getAverageFrameTiming(){if(this.coreTimeSamples.length!==0)return{coreTime:this.average(this.coreTimeSamples),renderTime:this.average(this.renderTimeSamples),totalTime:this.average(this.totalTimeSamples)}}getStats(){return{fps:this.fps,lastFrame:this.getLastFrameTiming(),avgFrame:this.getAverageFrameTiming(),sampleCount:this.coreTimeSamples.length,maxSamples:this.maxSamples}}reset(){this.frameCount=0,this.fps=0,this.fpsUpdateTime=0,this.lastCoreTime=0,this.lastRenderTime=0,this.lastTotalTime=0,this.coreTimeSamples=[],this.renderTimeSamples=[],this.totalTimeSamples=[]}average(t){return t.length===0?0:t.reduce((e,i)=>e+i,0)/t.length}};R(Y,"PerformanceMonitor");var L=Y;var Ie="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",A={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function $(){let S=atob(Ie),t=new Uint8Array(S.length);for(let e=0;e<S.length;e++)t[e]=S.charCodeAt(e);return t}R($,"decodeDefaultAtlas");var j=class j{constructor(t,e={}){d(this,"renderer");d(this,"options");d(this,"layerFilter",null);this.renderer=t,this.options={debug:e.debug??!1,useImageDataRendering:e.useImageDataRendering??!1}}setLayerFilter(t){this.layerFilter=t}getLayerFilter(){return this.layerFilter}async initialize(t){if("setImageDataRendering"in this.renderer&&this.options.useImageDataRendering&&(this.renderer.setImageDataRendering(!0),this.log("ImageData rendering enabled")),"setImageFont"in this.renderer&&typeof this.renderer.setImageFont=="function"){this.log("Loading default 8x8 font atlas...");let e=$();await this.renderer.setImageFont(e,A.glyphWidth,A.glyphHeight,A.cellWidth,A.cellHeight,A.atlasBlocks),this.log("Default 8x8 font atlas loaded")}await this.waitForReady(),this.log("Initialized and ready")}async waitForReady(t=100,e=50){this.log("Waiting for renderer to be ready");let i=0;return new Promise((r,n)=>{let s=R(()=>{if(i++,this.renderer.isReady())this.log(`Renderer ready after ${i*e}ms`),r();else if(i>=t){let a=`Renderer failed to be ready after ${t*e}ms`;this.log(a),n(new Error(a))}else setTimeout(s,e)},"check");s()})}async loadDefaultFont(t){console.warn("[RendererManager] loadDefaultFont() is deprecated. Use ImageFont instead.")}renderDisplay(t){return this.renderer.isReady()?t?(this.renderer.renderDisplayData(t),!0):(console.warn("[RENDERER MANAGER] No display provided"),!1):(console.warn("[RENDERER MANAGER] Renderer not ready"),!1)}render(t,e){let i=this.layerFilter?t.getRenderState(e,this.layerFilter):t.getRenderState(e);return!i||i.displays.length===0?(console.warn("[RENDERER MANAGER] No render state or no displays"),!1):this.renderDisplay(i.displays[0])}getRenderer(){return this.renderer}getCanvas(){return this.renderer.getCanvas()}getAvailableSize(){if(this.renderer.getAvailableSize)return this.renderer.getAvailableSize();let t=this.getCanvas();return{width:t?.clientWidth||t?.width||0,height:t?.clientHeight||t?.height||0}}isReady(){return this.renderer.isReady()}destroy(){this.log("Destroying renderer"),this.renderer.destroy()}log(t){this.options.debug&&console.warn(`[RendererManager] ${t}`)}};R(j,"RendererManager");var U=j;var N=class N{constructor(t){d(this,"core");d(this,"rendererManagers",new Map);d(this,"rendererInitPromises",new Map);d(this,"rendererManager");d(this,"rendererType");d(this,"primaryDisplayId",0);d(this,"displayConfigs");d(this,"options");d(this,"running",!1);d(this,"startTime",0);d(this,"lastTimestamp",0);d(this,"userId","");d(this,"visibilityChangeHandler");d(this,"tickRate",30);d(this,"accumulatedTime",0);d(this,"FRAME_TIME_MIN",1e3/60);d(this,"lastRenderTimestamp",0);d(this,"rafId",0);d(this,"firstTickDone",!1);d(this,"performanceMonitor");d(this,"renderMode","continuous");d(this,"renderRequested",!1);if(this.options={application:t.application,container:t.container,displayContainers:t.displayContainers,debug:t.debug??!1,width:t.width??80,height:t.height??25,renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,renderMode:t.renderMode??"continuous"},this.log("Initializing BaseClientRuntime"),this.core=new ae.Core({mode:"client",maxUsers:1}),this.core.onPaletteChanged(e=>{this.onCorePaletteChanged(e)}),this.core.onBitmapFontChanged(e=>{this.onCoreBitmapFontChanged(e)}),this.core.onImageFontChanged(e=>{this.onCoreImageFontChanged(e)}),this.visibilityChangeHandler=()=>{!document.hidden&&this.running&&(this.lastTimestamp=performance.now(),this.accumulatedTime=0,this.log("Tab visible: Reset timing"))},document.addEventListener("visibilitychange",this.visibilityChangeHandler),this.rendererType=this.options.renderer,this.options.displayContainers&&this.options.displayContainers.length>0){this.displayConfigs=new Map([[0,{container:this.options.container,renderer:this.options.renderer}]]);for(let e of this.options.displayContainers){let i=e.renderer;this.displayConfigs.set(e.id,{container:e.container,renderer:i})}this.primaryDisplayId=0}else this.displayConfigs=new Map([[0,{container:this.options.container,renderer:this.options.renderer}]]),this.primaryDisplayId=0;this.createRendererManagers(),this.performanceMonitor=new L(60),this.tickRate=20,this.renderMode=t.renderMode??"continuous",this.log(`Configured: tickRate=${this.tickRate}, renderMode=${this.renderMode}`),this.log("BaseClientRuntime initialized")}createRenderer(t,e){if(e==="terminal2d"){this.log("Creating Terminal 2D renderer");let s=new V.Terminal2D(t,{fixedCols:this.options.width,fixedRows:this.options.height,cellAspectRatio:1});return this.options.useImageDataRendering&&(this.log("Enabling ImageData rendering (pixel-perfect, optimized)"),s.setImageDataRendering(!0)),this.log("Canvas 2D renderer created successfully"),s}this.log("Creating TerminalGL renderer");let r=document.createElement("canvas").getContext("webgl");if(!r)throw new Error("WebGL not supported. TerminalGL requires WebGL 1.0.");if(!r.getExtension("OES_element_index_uint"))throw new Error("OES_element_index_uint extension not supported. TerminalGL requires this extension for large terminals (256x256).");if(!(t instanceof HTMLDivElement))throw new Error(`TerminalGL requires container to be an HTMLDivElement. Received: ${t.tagName}`);let n=new V.TerminalGL(t,{cols:this.options.width,rows:this.options.height,charWidth:8,charHeight:8});return this.log("TerminalGL created successfully"),n}createRendererManagers(){let t=this.displayConfigs.size>=3;this.rendererManagers.clear();for(let[i,r]of this.displayConfigs.entries()){let n=t?"terminal2d":r.renderer??this.options.renderer,s=this.createRenderer(r.container,n??"webgl"),o=new U(s,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering});this.rendererManagers.set(i,o)}let e=this.rendererManagers.get(this.primaryDisplayId);if(!e)throw new Error(`Primary display ${this.primaryDisplayId} has no renderer. Check displayContainers configuration.`);this.rendererManager=e}ensureRendererManager(t){let e=this.rendererManagers.get(t);if(e)return e;let i=document.createElement("div");i.dataset.displayId=String(t),i.style.cssText="position:relative;display:inline-block;margin:4px;background:#000;overflow:hidden;",(this.options.container?.parentElement??document.body).appendChild(i),this.displayConfigs.set(t,{container:i});let o=this.displayConfigs.size>=3?"terminal2d":this.options.renderer??"webgl",a=this.createRenderer(i,o),l=new U(a,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering});if(this.rendererManagers.set(t,l),!this.rendererInitPromises.has(t)){let c=l.initialize(this.core).then(()=>{this.onCorePaletteChanged(this.core.getPalette());let h=this.core.getImageFontIds,u=typeof h=="function"?h.call(this.core):[];u.length>0&&this.onCoreImageFontChanged(u[0]);let m=this.rebuildPostProcessOverlays;typeof m=="function"&&m.call(this)}).catch(h=>{console.warn(`[BaseClientRuntime] Failed to init renderer for display ${t}:`,h)});this.rendererInitPromises.set(t,c)}return l}getRendererType(){return this.rendererType}isRunning(){return this.running}setTickRate(t){if(t<0||t>1e3)throw new Error(`Invalid tick rate: ${t}. Must be between 0 and 1000.`);if(this.tickRate=t,this.userId){let e=this.core.getUser(this.userId);e&&e.setBytesTickRate(t>0?t:20)}t===0&&this.renderMode==="continuous"?(this.renderMode="on-demand",this.log("Tick rate set to 0, switched to on-demand render mode")):this.log(`Tick rate set to ${t} TPS`)}setRenderMode(t){this.renderMode=t,this.log(`Render mode set to ${t}`)}setMaxFPS(t){if(t<=0||t>240)throw new Error(`Invalid FPS: ${t}. Must be between 1 and 240.`);this.FRAME_TIME_MIN=1e3/t,this.log(`Max FPS set to ${t}`)}getTickRate(){return this.tickRate}requestRender(){this.renderMode==="on-demand"&&(this.renderRequested=!0,this.log("Render requested"))}async start(){if(this.running){this.log("Already running");return}this.log("Starting BaseClientRuntime"),await this.onBeforeStart();for(let[t,e]of this.rendererManagers.entries())await e.initialize(this.core),this.log(`Renderer for display ${t} is ready`);this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderers"),this.options.application&&(this.log("Calling application.init()"),await this.options.application.init(this.core,this)),await this.onAfterInit(),await this.createLocalUser(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onBeforeStart(){}async onAfterInit(){}async stop(){if(!this.running)return;this.log("Stopping BaseClientRuntime"),this.running=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),await this.onStop();let t=this.core.getUser(this.userId);t&&this.options.application?.destroyUser&&this.options.application.destroyUser(this.core,t,"Runtime stopped"),this.log("BaseClientRuntime stopped")}async onStop(){}getStats(){let t=this.performanceMonitor.getStats();return{running:this.running,userCount:this.core.getUsers().length,fps:t.fps,uptime:this.running?performance.now()-this.startTime:0,totalFrames:0,lastFrameTiming:t.lastFrame,avgFrameTiming:t.avgFrame}}getTotalBytesReceived(){return this.core.getUser(this.userId)?.getTotalBytesReceived()??0}getBytesReceivedPerSecond(){return this.core.getUser(this.userId)?.getBytesReceivedPerSecond()??0}resetBytesReceived(){let t=this.core.getUser(this.userId);t&&t.resetByteCounters()}async destroy(){await this.stop(),this.log("Destroying BaseClientRuntime"),this.visibilityChangeHandler&&(document.removeEventListener("visibilitychange",this.visibilityChangeHandler),this.visibilityChangeHandler=void 0),await this.onDestroy();for(let t of this.rendererManagers.values()){let e=t.getRenderer();e&&"clearOnResizeCallback"in e&&e.clearOnResizeCallback()}this.options.application?.destroy&&this.options.application.destroy();for(let t of this.rendererManagers.values())t.destroy();this.log("BaseClientRuntime destroyed")}async onDestroy(){}async createLocalUser(){this.userId="local",this.log(`Creating local user: ${this.userId}`);let t=this.core.createUser(this.userId,"User");t.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.onUserCreated(t),this.options.application?.initUser&&(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,t,{username:"User"})),this.processInitialOrders(t);let e=this.core.generateAllLoadPackets();e.forEach(s=>{t.recordBytesReceived(s.length)});let i=this.core.generateMacroLoadPackets(this.userId);i.forEach(s=>{t.recordBytesReceived(s.length)});let r=t.getInputBindingsLoadPacket();r&&t.recordBytesReceived(r.length),this.log(`Simulated ${e.length} load packets, ${i.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach(({static:s,dynamic:o},a)=>{let l=this.core.getUser(a);l&&(s&&l.recordBytesReceived(s.length),o&&l.recordBytesReceived(o.length))})}processInitialOrders(t){if(t.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let e=t.flushMacroOrders();t.applyMacroOrders(e)}}onUserCreated(t){}mainLoop(t){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(t),this.render()),this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}let e=t-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&e<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.lastRenderTimestamp=t;let i=(t-this.lastTimestamp)/1e3,r=Math.min(i,.1);this.lastTimestamp=t,this.performanceMonitor.updateFPS(t);let n=this.core.getUser(this.userId);if(!n){this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}if(this.tickRate===0){let l=performance.now();this.render();let c=performance.now()-l;this.performanceMonitor.recordFrameTiming(0,c),this.rafId=requestAnimationFrame(h=>this.mainLoop(h));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=t,this.accumulatedTime=0,this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.accumulatedTime+=r;let s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let o=0,a=0;for(;this.accumulatedTime>=s&&o<5;){let l=performance.now();this.collectAndApplyInput(n),this.options.application&&(this.options.application.update?.(this.core,s),this.options.application.updateUser?.(this.core,n,s)),this.processTickOrders(n);let c=n.updateMacros();n.processMacroEvents(c),this.core.endTick().forEach(({static:u,dynamic:m})=>{let v=(u?.length??0)+(m?.length??0);n.recordBytesReceived(v)}),a+=performance.now()-l,this.accumulatedTime-=s,o++}if(o>0){let l=performance.now();this.render();let c=performance.now()-l;this.performanceMonitor.recordFrameTiming(a,c)}this.rafId=requestAnimationFrame(l=>this.mainLoop(l))}collectAndApplyInput(t){}processTickOrders(t){if(t.clearTextInputs(),t.hasPendingMacroOrders()){let e=t.flushMacroOrders();t.applyMacroOrders(e)}}render(){let t=this.core.getUser(this.userId);if(t){let i=t.getMacroRenderOrders();for(let[r,n]of i){let s=t.getLayerById(r);s&&n.length>0&&s.addTemporaryOrders(n)}}let e=this.core.getRenderState(this.userId);if(!e||e.displays.length===0){console.warn("[BaseClientRuntime] No render state or displays");return}for(let i of e.displays){let r=this.rendererManagers.get(i.id)??this.ensureRendererManager(i.id);if(!r){console.warn(`[BaseClientRuntime] No renderer manager for display ${i.id}, skipping render`);continue}let n=r.getLayerFilter(),s=n?this.core.getRenderState(this.userId,n):e;if(!s){this.options.debug&&this.log(`Render state unavailable for display ${i.id} with filter`);continue}let o=s.displays.find(a=>a.id===i.id);if(!o){this.options.debug&&this.log(`Render state missing display ${i.id} after filtering`);continue}if(!r.isReady()){this.options.debug&&this.log(`Renderer for display ${i.id} not ready yet`);continue}r.renderDisplay(o)}}onCorePaletteChanged(t){this.log(`Palette changed, updating renderer (${t.size} colors)`);let e=[];for(let n=0;n<256;n++){let s=t.get(n);s?e.push({r:s.r,g:s.g,b:s.b,a:s.a}):e.push({r:0,g:0,b:0,a:0})}let i=R(n=>{n&&"setPalette"in n&&typeof n.setPalette=="function"&&n.setPalette(e)},"applyPalette"),r=0;for(let n of this.rendererManagers.values()){let s=n.getRenderer();s&&(i(s),r++)}r===0?console.warn("[BaseClientRuntime] Cannot update palette: no renderer available"):this.log(`\u2713 Palette updated on ${r} renderer(s)`)}onCoreBitmapFontChanged(t){this.log(`Bitmap font ${t} changed, updating renderer`);let e=this.core.getBitmapFont(t);if(!e){console.warn(`[BaseClientRuntime] Font ${t} not found in registry`);return}let i=new Map,r=0;for(let o=0;o<256;o++){let a=e.getGlyph(o);a&&(i.set(o,a),r++)}this.log(`Built bitmap font map with ${r} glyphs, dimensions: ${e.getCharWidth()}x${e.getCharHeight()}`);let n=R(o=>{o&&"setBitmapFont"in o&&typeof o.setBitmapFont=="function"&&o.setBitmapFont(i,e.getCharWidth(),e.getCharHeight(),e.getCellWidth(),e.getCellHeight())},"applyBitmapFont"),s=0;for(let[o,a]of this.rendererManagers.entries()){let l=a.getRenderer();l&&(n(l),s++,this.onFontChanged(l,o))}s===0?console.warn("[BaseClientRuntime] Cannot update font: no renderer available"):this.log(`\u2713 Renderer bitmap font updated successfully on ${s} renderer(s)`)}onCoreImageFontChanged(t){this.log(`Image font ${t} changed, updating renderer`);let e=this.core.getImageFont(t);if(!e){console.warn(`[BaseClientRuntime] Image font ${t} not found in registry`);return}this.log(`Loading image font with ${e.getAtlasBlocks()} blocks, glyph: ${e.getGlyphWidth()}x${e.getGlyphHeight()}`);let i=R(n=>{n&&"setImageFont"in n&&typeof n.setImageFont=="function"&&n.setImageFont(e.getImageData(),e.getGlyphWidth(),e.getGlyphHeight(),e.getCellWidth(),e.getCellHeight(),e.getAtlasBlocks())},"applyImageFont"),r=0;for(let[n,s]of this.rendererManagers.entries()){let o=s.getRenderer();o&&(i(o),r++,this.onFontChanged(o,n))}r===0?console.warn("[BaseClientRuntime] Cannot update image font: no renderer available"):this.log(`\u2713 Renderer image font updated successfully on ${r} renderer(s)`)}onFontChanged(t,e){}log(t){this.options.debug&&console.warn(`[BaseClientRuntime] ${t}`)}getCore(){return this.core}getRendererManager(t=this.primaryDisplayId){let e=this.rendererManagers.get(t);if(!e)throw new Error(`Renderer manager for display ${t} not found`);return e}setDisplayContainers(t){this.displayConfigs=new Map(t.map(i=>[i.id,{container:i.container}]));let e=Array.from(this.displayConfigs.keys());this.primaryDisplayId=e.length>0?Math.min(...e):0;for(let i of this.rendererManagers.values())i.destroy();this.rendererManagers.clear(),this.createRendererManagers()}getUserId(){return this.userId}};R(N,"BaseClientRuntime");var O=N;var w=require("@utsp/core"),q=require("@utsp/render"),B=require("@utsp/types"),M=require("@utsp/input"),le=require("@utsp/network-client"),ce=require("@utsp/audio");var de=require("@utsp/core"),x=require("@utsp/input");var P=1e3,Z=class Z{constructor(t,e,i,r,n,s,o){d(this,"network");d(this,"core");d(this,"rendererManagers");d(this,"primaryDisplayId");d(this,"performanceMonitor");d(this,"options");d(this,"layerTraffic",new Map);d(this,"miscTraffic",new Map);d(this,"displayTraffic",new Map);d(this,"audioManager",null);d(this,"postProcessCallback",null);d(this,"userId","");d(this,"lastReceivedTick",-1);d(this,"connected",!1);d(this,"totalBytesReceived",0);d(this,"lastSentViewports",new Map);d(this,"lastCollectedTouches",[]);d(this,"touchZonesCallback",null);d(this,"audioLoadingState",{totalExpected:0,loadedSounds:new Set,errors:new Map});d(this,"ensureRendererManager");this.network=t,this.core=e,this.rendererManagers=i,this.primaryDisplayId=s,this.performanceMonitor=r,this.ensureRendererManager=o,this.options={serverUrl:n.serverUrl,username:n.username,token:n.token??"",debug:n.debug??!1,autoReconnect:n.autoReconnect??!0,canvasWidth:n.canvasWidth,canvasHeight:n.canvasHeight}}getRendererManager(t){return this.rendererManagers.get(t)}getPrimaryRendererManager(){return this.rendererManagers.get(this.primaryDisplayId)??Array.from(this.rendererManagers.values())[0]}renderDisplays(){let t=this.core.getRenderState(this.userId);if(!t||t.displays.length===0){this.options.debug&&this.log("Render skipped: no render state or displays");return}for(let e of t.displays){let i=this.getRendererManager(e.id)??this.ensureRendererManager?.(e.id);if(!i){console.warn(`[NetworkSync] No renderer manager for display ${e.id}, skipping render`);continue}let r=i.getLayerFilter?.(),n=r?this.core.getRenderState(this.userId,r):t;if(!n){this.options.debug&&this.log(`Render state unavailable for display ${e.id} with filter`);continue}let s=n.displays.find(o=>o.id===e.id);if(!s){this.options.debug&&this.log(`Render state missing display ${e.id} after filtering`);continue}if(!i.isReady()){this.options.debug&&this.log(`Renderer for display ${e.id} not ready yet`);continue}i.renderDisplay(s)}}async connect(){this.log(`Connecting to ${this.options.serverUrl}`),await this.network.connect(),this.connected=!0,this.log("Connected to server"),this.setupNetworkHandlers()}async joinGame(t){return new Promise((e,i)=>{this.network.send("join",{username:this.options.username,token:this.options.token}),this.network.on("join_response",r=>{if(r.success){this.userId=r.userId,this.log(`Joined game as ${this.userId}`);let n=this.core.createUser(this.userId,this.options.username);n.setBytesTickRate(20),this.audioManager&&n.setAudioProcessor(this.audioManager),t?.initUser&&t.initUser(this.core,n,{username:this.options.username,token:this.options.token}),e(this.userId)}else i(new Error(r.error||"Failed to join game"))}),setTimeout(()=>i(new Error("Join timeout")),5e3)})}sendInput(t){let e=this.core.getUser(this.userId);if(!e)return;let i=this.getPrimaryRendererManager(),r=i?.getCanvas();if(r){let g=e.getDisplays(),te=(g.find(F=>F.getId&&F.getId()===this.primaryDisplayId)??g[0])?.getSize()??{x:this.options.canvasWidth,y:this.options.canvasHeight},ie=te.x,re=te.y,ne=i?.getRenderer()?.getOffsets?.(),se={offsetX:ne?.offsetX??0,offsetY:ne?.offsetY??0},E=x.InputCollector.collectMousePosition(t,r,ie,re,se);e.setMousePosition(E.x,E.y,E.over),e.updateMacroMouse(E.x,E.y,E.isLeftDown);let oe=x.InputCollector.collectTouchPositions(t,r,ie,re,10,se);oe.forEach(F=>{e.setTouchPosition(F.id,F.x,F.y,F.over)}),this.lastCollectedTouches=oe}else this.lastCollectedTouches=[];let n=e.getInputBindingRegistry(),s=n.getAllAxes(),o=n.getAllButtons(),a=x.InputCollector.collectAxisSources(s,t),l=x.InputCollector.collectButtonSources(o,t),c=x.InputCollector.collectTextInputs(t);c.length>0&&e.setTextInputs(c);let h=s.sort((g,I)=>g.bindingId-I.bindingId),u=new Map;for(let g of h){let I=n.evaluateAxis(g.bindingId,a);u.set(g.bindingId,I),e.setAxis(g.name,I)}let m=o.sort((g,I)=>g.bindingId-I.bindingId),v=new Map;for(let g of m){let I=n.evaluateButton(g.bindingId,l);v.set(g.bindingId,I),e.setButton(g.name,I)}let f=e.getMouseDisplayInfo(),C=e.getIsMouseOnADisplay(),b=this.collectChangedViewports(),p=this.lastCollectedTouches.map(g=>({id:g.id,x:g.x,y:g.y,over:g.over})),y=(0,de.encodeCompressedInput)(0n,u,v,f?.displayId??0,f?.localX??0,f?.localY??0,C,c,[],b,p);this.network.send("input",y)}collectChangedViewports(){let t=[];for(let[e,i]of this.rendererManagers.entries()){let{width:r,height:n}=i.getAvailableSize();if(r===0||n===0)continue;let s=this.lastSentViewports.get(e);(!s||s.pixelWidth!==r||s.pixelHeight!==n)&&(t.push({displayId:e,pixelWidth:r,pixelHeight:n}),this.lastSentViewports.set(e,{pixelWidth:r,pixelHeight:n}),this.log(`Viewport changed: display ${e} = ${r}x${n}px`))}return t}disconnect(){this.connected&&(this.network.send("leave",{}),this.network.disconnect(),this.connected=!1,this.log("Disconnected from server"))}getUserId(){return this.userId}isConnected(){return this.connected}getTotalBytesReceived(){return this.totalBytesReceived}getAudioLoadingState(){let t=this.audioLoadingState.loadedSounds.size,e=this.audioLoadingState.totalExpected,i=this.audioLoadingState.errors.size,r=e>0&&t>=e,n=Array.from(this.audioLoadingState.errors.keys());return{loadedCount:t,totalExpected:e,errorCount:i,isComplete:r,errors:n}}destroy(){this.disconnect(),this.network.destroy(),this.log("NetworkSync destroyed")}setupNetworkHandlers(){this.network.on("update",t=>{try{let e=this.convertToUint8Array(t,"update");if(!e)return;this.recordBytesReceived(e.length);let i=this.core.applyUpdatePacketBuffer(this.userId,e);i?(this.postProcessCallback&&i.postProcessOrders&&i.postProcessOrders.length>0&&this.postProcessCallback(i.postProcessOrders),this.renderDisplays()):this.log("Failed to apply update packet")}catch(e){this.log(`Error applying update packet: ${e}`)}}),this.network.on("update-static",t=>{this.handleUpdatePacket(t,"update-static")}),this.network.on("update-dynamic",t=>{this.handleUpdatePacket(t,"update-dynamic")}),this.network.on("load",t=>{try{let e=this.convertToUint8Array(t,"load");if(!e)return;this.recordBytesReceived(e.length),this.core.applyLoadPacket(e)?this.log("Load packet applied successfully"):this.log("Failed to apply load packet")}catch(e){this.log(`Error applying load packet: ${e}`)}}),this.network.on("input-bindings",t=>{if(this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)){this.log("Input bindings configured");try{let i=JSON.parse(t);if(this.log(`Touch zones in packet: ${i.touchZones?.length??0}, callback defined: ${!!this.touchZonesCallback}`),i.touchZones&&i.touchZones.length>0&&this.touchZonesCallback){let r=i.touchZones.map(n=>({id:n.zoneId,x:n.x,y:n.y,width:n.width,height:n.height}));this.touchZonesCallback(r),this.log(`Touch zones configured: ${r.length} zones`)}}catch{}}else this.log("Failed to apply input bindings")}),this.audioManager&&this.setupSoundHandlers(),this.network.on("disconnect",()=>{this.connected=!1,this.log("Disconnected from server")})}handleUpdatePacket(t,e){try{let i=this.convertToUint8Array(t,e);if(!i)return;this.recordBytesReceived(i.length);let r=new DataView(i.buffer,i.byteOffset,i.byteLength),n=Number(r.getBigUint64(0,!1));if(e==="update-dynamic"&&n<this.lastReceivedTick)return;n>this.lastReceivedTick&&(this.lastReceivedTick=n);let s=this.core.applyUpdatePacketBuffer(this.userId,i);if(s){let o=performance.now(),a=R((b,p)=>{if(p<=0)return;let y=this.miscTraffic.get(b)??{total:0,windowSum:0,samples:[]};y.total+=p,y.windowSum+=p,y.samples.push({time:o,bytes:p}),this.miscTraffic.set(b,y)},"recordMisc"),l=R(b=>{try{return JSON.stringify(b).length}catch{return 0}},"safeLen"),c=s.__byteSizes,h=c?.audioOrders;typeof h=="number"?a("update.audioOrders",h):s.audioOrders&&s.audioOrders.length&&a("update.audioOrders",l(s.audioOrders));let u=c?.vibrationOrders;typeof u=="number"?a("update.vibrationOrders",u):s.vibrationOrders&&s.vibrationOrders.length&&a("update.vibrationOrders",l(s.vibrationOrders));let m=c?.macroOrders;typeof m=="number"?a("update.macroOrders",m):s.macroOrders&&s.macroOrders.length&&a("update.macroOrders",l(s.macroOrders));let v=c?.postProcessOrders;if(typeof v=="number"?a("update.postProcessOrders",v):s.postProcessOrders&&s.postProcessOrders.length&&a("update.postProcessOrders",l(s.postProcessOrders)),s.displays&&s.displays.length)for(let b of s.displays){let p=b.id;if(typeof p!="number")continue;let y=this.displayTraffic.get(p)??{total:0,windowSum:0,samples:[]},g=7;y.total+=g,y.windowSum+=g,y.samples.push({time:o,bytes:g}),this.displayTraffic.set(p,y)}for(let b of s.layers){let p=b.byteSize??0,y=this.layerTraffic.get(b.id)??{total:0,windowSum:0,samples:[]};y.total+=p,y.windowSum+=p,y.samples.push({time:o,bytes:p});let g=o-P;for(;y.samples.length&&y.samples[0].time<g;){let I=y.samples.shift();I&&(y.windowSum-=I.bytes)}this.layerTraffic.set(b.id,y)}{let b=o-P;this.miscTraffic.forEach((p,y)=>{for(;p.samples.length&&p.samples[0].time<b;){let g=p.samples.shift();g&&(p.windowSum-=g.bytes)}p.windowSum<0&&(p.windowSum=0),this.miscTraffic.set(y,p)})}{let b=o-P;this.displayTraffic.forEach((p,y)=>{for(;p.samples.length&&p.samples[0].time<b;){let g=p.samples.shift();g&&(p.windowSum-=g.bytes)}p.windowSum<0&&(p.windowSum=0),this.displayTraffic.set(y,p)})}this.postProcessCallback&&s.postProcessOrders&&s.postProcessOrders.length>0&&this.postProcessCallback(s.postProcessOrders),this.options.debug&&this.debugRenderState();let f=performance.now();this.renderDisplays();let C=performance.now()-f;this.performanceMonitor.recordFrameTiming(0,C),this.options.debug&&console.warn(`[CLIENT] render() completed for ${e} (${C.toFixed(2)}ms)`)}else this.options.debug&&this.log(`Failed to apply ${e} packet (tick ${n})`)}catch(i){this.log(`Error applying ${e} update packet: ${i}`),console.error(i)}}convertToUint8Array(t,e){return t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):Array.isArray(t)?new Uint8Array(t):t.data&&Array.isArray(t.data)?new Uint8Array(t.data):typeof t=="object"&&t.type==="Buffer"?new Uint8Array(t.data):(this.log(`Unknown data type for ${e} packet: ${typeof t}`),null)}debugRenderState(){let t=this.core.getUser(this.userId);if(t){let r=t.getLayers();console.warn(`[CLIENT] User has ${r.length} layers`),r.forEach((n,s)=>{let o=n.getOrders(),a=n.getStatic();(o.length>0||a)&&console.warn(` Layer ${s}: ${o.length} orders, static=${a}, z=${n.getZOrder()}`)})}let i=this.getPrimaryRendererManager()?.isReady()??!1;console.warn(`[CLIENT] Renderer ready: ${i}`)}log(t){this.options.debug&&console.warn(`[NetworkSync] ${t}`)}recordBytesReceived(t){this.totalBytesReceived+=t;let e=this.core.getUser(this.userId);e&&e.recordBytesReceived(t)}getLayerTrafficStats(){let t={},i=performance.now()-P;return this.layerTraffic.forEach((r,n)=>{for(;r.samples.length&&r.samples[0].time<i;){let s=r.samples.shift();s&&(r.windowSum-=s.bytes)}r.windowSum<0&&(r.windowSum=0),t[n]={total:r.total,bytesPerSec1s:r.windowSum/(P/1e3)}}),t}getMiscTrafficStats(){let t={},i=performance.now()-P;return this.miscTraffic.forEach((r,n)=>{for(;r.samples.length&&r.samples[0].time<i;){let s=r.samples.shift();s&&(r.windowSum-=s.bytes)}r.windowSum<0&&(r.windowSum=0),t[n]={total:r.total,bytesPerSec1s:r.windowSum/(P/1e3)}}),t}getDisplayTrafficStats(){let t={},i=performance.now()-P;return this.displayTraffic.forEach((r,n)=>{for(;r.samples.length&&r.samples[0].time<i;){let s=r.samples.shift();s&&(r.windowSum-=s.bytes)}r.windowSum<0&&(r.windowSum=0),t[n]={total:r.total,bytesPerSec1s:r.windowSum/(P/1e3)}}),t}getSoundPacketSize(t){return t.mode==="file"?t.sounds.reduce((i,r)=>i+this.getSoundDataLength(r.data)+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,0)}getSoundDataLength(t){if(t instanceof Uint8Array)return t.length;if(t instanceof ArrayBuffer||ArrayBuffer.isView(t))return t.byteLength;let e=globalThis.Buffer;return e&&t instanceof e?t.length:t&&typeof t=="object"&&Array.isArray(t.data)?t.data.length:Array.isArray(t)?t.length:0}setAudioManager(t){if(this.audioManager=t,t&&this.userId){let e=this.core.getUser(this.userId);e&&e.setAudioProcessor(t)}this.connected&&t&&this.setupSoundHandlers()}setPostProcessCallback(t){this.postProcessCallback=t}setTouchZonesCallback(t){this.touchZonesCallback=t}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),t.totalSounds!==void 0&&t.totalSounds>this.audioLoadingState.totalExpected&&(this.audioLoadingState.totalExpected=t.totalSounds,this.log(`Audio loading: expecting ${t.totalSounds} total sounds`)),t.mode==="file"?await this.handleSoundLoad(t):t.mode==="external"&&await this.handleSoundExternalLoad(t)}),this.log("Sound handlers registered"))}async handleSoundLoad(t){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}for(let i of t.sounds)try{let r=this.normalizeSoundData(i.data);await e.loadFromData(i.soundId,i.name,r),this.log(`Loaded sound: ${i.name} (${r.length} bytes)`),this.audioLoadingState.loadedSounds.add(i.name),this.audioLoadingState.errors.delete(i.name),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.audioLoadingState.errors.set(i.name,String(r)),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}normalizeSoundData(t){if(t instanceof Uint8Array)return t;if(t instanceof ArrayBuffer)return new Uint8Array(t);if(ArrayBuffer.isView(t))return new Uint8Array(t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength));let e=globalThis.Buffer;if(e&&t instanceof e)return new Uint8Array(t);if(t&&typeof t=="object"&&Array.isArray(t.data))return new Uint8Array(t.data);if(Array.isArray(t))return new Uint8Array(t);throw new Error("Invalid sound data payload")}async handleSoundExternalLoad(t){if(!this.audioManager){this.log("Cannot load external sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load external sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromUrl(i.soundId,i.name,i.url),this.log(`Loaded external sound: ${i.name} from ${i.url}`),this.audioLoadingState.loadedSounds.add(i.name),this.audioLoadingState.errors.delete(i.name),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,r),this.audioLoadingState.errors.set(i.name,String(r)),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}sendAudioAck(t){this.network.send("audio-ack",t),this.log(`Sent audio-ack: ${t.type}`)}sendBridge(t,e){this.network.sendBridge(t,e),this.log(`Bridge sent on channel '${t}'`)}onBridge(t,e){this.network.onBridge(t,e),this.log(`Bridge handler registered for channel '${t}'`)}offBridge(t,e){this.network.offBridge(t,e),this.log(`Bridge handler removed for channel '${t}'`)}removeAllBridgeHandlers(t){this.network.removeAllBridgeHandlers(t),this.log(t?`All bridge handlers removed for '${t}'`:"All bridge handlers removed")}};R(Z,"NetworkSync");var G=Z;var J=class J extends O{constructor(e){super({application:e.application,container:e.container,displayContainers:e.displayContainers,debug:e.debug,width:e.width,height:e.height,renderer:e.renderer,useImageDataRendering:e.useImageDataRendering,renderMode:e.renderMode});d(this,"input",null);d(this,"networkSync",null);d(this,"mode");d(this,"localOptions",null);d(this,"autoplayOverlay",null);d(this,"autoplay",!0);d(this,"audioManager",null);d(this,"mobileVibration",null);d(this,"postProcessOverlay",null);d(this,"postProcessOverlays",new Map);d(this,"postProcessOrderCollector",new w.PostProcessOrderCollector);d(this,"pendingPaletteSlotByDisplay",new Map);d(this,"localBridgeHandlers",new Map);d(this,"localBridgeWildcardHandlers",new Set);if(this.mode=e.mode,e.mode==="local"?(this.localOptions={mode:"local",application:e.application,container:e.container,debug:e.debug??!1,width:e.width??80,height:e.height??25,userId:e.userId??"local",username:e.username??"User",renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,touchZones:e.touchZones,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.userId=e.userId??"local",this.autoplay=e.autoplay??!0):(this.localOptions={mode:"connected",application:e.application,container:e.container,serverUrl:e.serverUrl,username:e.username??"Player",debug:e.debug??!1,width:e.width??80,height:e.height??25,autoReconnect:e.autoReconnect??!0,token:e.token,renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,touchZones:e.touchZones,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.autoplay=e.autoplay??!0),this.log(`Initializing ClientRuntime (${this.mode} mode)`),this.mode==="connected"&&(this.core=new w.Core({mode:"client",maxUsers:100}),this.core.onPaletteChanged(r=>{this.onCorePaletteChanged(r),this.retryPendingPaletteSelections()}),this.core.onBitmapFontChanged(r=>{this.onCoreBitmapFontChanged(r)}),this.core.onImageFontChanged(r=>{this.onCoreImageFontChanged(r)})),e.inputEnabled??!0?this.input=new M.UnifiedInputRouter({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:e.touchZones?.targetElement??void 0,debug:e.debug,keyboardConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mouseConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mobileConfig:{preventDefault:e.mobileInputConfig?.preventDefault??!1,passive:e.mobileInputConfig?.passive??!0,maxTouches:e.mobileInputConfig?.maxTouches??10},enableTouchZones:e.touchZones?.enable??!1,touchZoneConfig:{targetElement:e.touchZones?.targetElement,gridWidth:e.touchZones?.gridWidth??e.width??0,gridHeight:e.touchZones?.gridHeight??e.height??0,rendererOffsets:e.touchZones?.rendererOffsets,zones:e.touchZones?.zones}}):this.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new le.SocketIOClient({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:e.debug});this.networkSync=new G(n,this.core,this.rendererManagers,this.performanceMonitor,{serverUrl:r.serverUrl,username:r.username,token:r.token,debug:e.debug,autoReconnect:r.autoReconnect,canvasWidth:e.width??80,canvasHeight:e.height??25},this.primaryDisplayId,s=>this.ensureRendererManager(s))}this.log("ClientRuntime initialized")}rebuildPostProcessOverlays(){for(let e of this.postProcessOverlays.values())e.destroy();this.postProcessOverlays.clear(),this.postProcessOverlay=null;for(let[e,i]of this.displayConfigs.entries()){let r=new q.PostProcessOverlay(i.container);this.postProcessOverlays.set(e,r),e===this.primaryDisplayId&&(this.postProcessOverlay=r)}!this.postProcessOverlay&&this.postProcessOverlays.size>0&&(this.postProcessOverlay=Array.from(this.postProcessOverlays.values())[0]??null)}getMode(){return this.mode}setTickRate(e){if(this.mode==="connected"){this.log("setTickRate() has no effect in connected mode");return}super.setTickRate(e)}async onBeforeStart(){this.audioManager=new ce.AudioManager({debug:this.options.debug}),this.mobileVibration=new M.MobileVibration({debug:this.options.debug}),this.autoplay?(this.log("Autoplay enabled, initializing AudioManager..."),this.audioManager.initialize()):(this.log("Autoplay disabled, showing overlay..."),await new Promise(e=>{this.autoplayOverlay=new q.AutoplayOverlay(this.options.container,{...this.localOptions?.autoplayOptions,onStart:()=>{this.log("User clicked start button"),this.audioManager&&(this.audioManager.initialize(),this.audioManager.playStartSound()),this.mobileVibration&&this.mobileVibration.vibrate(50),e()}})}),this.log("Autoplay overlay dismissed, continuing startup..."))}async onAfterInit(){this.rebuildPostProcessOverlays(),this.log("PostProcessOverlays created");let e=this.rendererManager.getCanvas();if(e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync){if(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),this.input){let r=this.input,n=this.rendererManager;this.networkSync.setTouchZonesCallback(s=>{this.log(`[onAfterInit] Touch zones callback called with ${s.length} zones`);let o=n.getCanvas();if(this.log(`[onAfterInit] Canvas available: ${!!o}`),o){let a=s.reduce((h,u)=>Math.max(h,u.x+u.width),0),l=s.reduce((h,u)=>Math.max(h,u.y+u.height),0);this.log(`[onAfterInit] Grid from zones: ${a}x${l}`);let c=r.enableTouchZonesDevice(o,a,l,s);this.log(`[onAfterInit] enableTouchZonesDevice returned: ${c}`),c&&this.log(`Touch zones configured from server: ${s.length} zones`)}})}if(await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application),this.input){let n=this.core.getUser(this.userId)?.getDisplays()??[];if(n.length>0){let s=n[0].getSize();this.input.updateTouchZoneLayout(s.x,s.y)}}let i=this.core.getUser(this.userId);i&&this.configureUserProcessors(i)}}async start(){if(this.running){this.log("Already running");return}this.log("Starting ClientRuntime"),await this.onBeforeStart();for(let[r,n]of this.rendererManagers.entries())await n.initialize(this.core),this.log(`Renderer for display ${r} is ready`);this.rebuildPostProcessOverlays(),this.log("PostProcessOverlays created"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.options.application&&(this.log("Calling application.init()"),await this.options.application.init(this.core,this));let e=this.rendererManager.getCanvas();if(e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync){if(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(n=>{this.applyPostProcessOrders(n)}),this.input){let n=this.input,s=this.rendererManager;this.networkSync.setTouchZonesCallback(o=>{this.log(`[start] Touch zones callback called with ${o.length} zones`);let a=s.getCanvas();if(this.log(`[start] Canvas available: ${!!a}`),a){let l=o.reduce((u,m)=>Math.max(u,m.x+m.width),0),c=o.reduce((u,m)=>Math.max(u,m.y+m.height),0);this.log(`[start] Grid from zones: ${l}x${c}`);let h=n.enableTouchZonesDevice(a,l,c,o);this.log(`[start] enableTouchZonesDevice returned: ${h}`),h&&this.log(`Touch zones configured from server: ${o.length} zones`)}})}await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application);let r=this.core.getUser(this.userId);r&&this.configureUserProcessors(r)}else await this.createLocalUser();let i=this.core.getUser(this.userId);if(i){let r=i.getDisplays();if(r.length>0){let s=r[0].getSize(),o=s.x,a=s.y,l=this.rendererManager.getRenderer(),c,h;if(this.rendererType==="webgl"){let m=l.getGridSize();c=m.cols,h=m.rows}else{let u=l;c=u.getCols(),h=u.getRows()}(c!==o||h!==a)&&(this.log(`Adjusting renderer from ${c}\xD7${h} to match display ${o}\xD7${a}`),l.resize(o,a))}}this.input&&this.input.start(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onStop(){this.audioManager&&this.audioManager.stopAll(),this.input&&this.input.stop(),this.mode==="connected"&&this.networkSync&&this.networkSync.disconnect()}getStats(){let e=super.getStats(),r=this.core.getUser(this.userId)?.getTotalBytesReceived();return{...e,mode:this.mode,latency:void 0,totalBytesReceived:r}}getAudioLoadingState(){if(this.mode==="connected"&&this.networkSync)return this.networkSync.getAudioLoadingState();if(this.audioManager){let e=this.audioManager.getSoundBank();if(e){let i=e.getStats();return{loadedCount:i.totalSounds,totalExpected:i.totalSounds,errorCount:0,isComplete:!0,errors:[]}}}return{loadedCount:0,totalExpected:0,errorCount:0,isComplete:!0,errors:[]}}async onDestroy(){this.autoplayOverlay&&(this.autoplayOverlay.destroy(),this.autoplayOverlay=null),this.audioManager&&(this.audioManager.destroy(),this.audioManager=null),this.postProcessOverlay&&(this.postProcessOverlay.destroy(),this.postProcessOverlay=null);for(let e of this.postProcessOverlays.values())e.destroy();this.postProcessOverlays.clear(),this.input&&this.input.destroy(),this.networkSync&&this.networkSync.destroy()}configureUserProcessors(e){if(this.audioManager&&e.setAudioProcessor(this.audioManager),this.mobileVibration&&e.setMobileVibrationProcessor(this.mobileVibration),this.input){let i=this.input.getGamepad();i&&e.setGamepadVibrationProcessor(i)}}async createLocalUser(){let e=this.localOptions;this.userId=e?.userId??"local",this.log(`Creating local user: ${this.userId}`);let i=this.core.createUser(this.userId,e?.username??"User");if(i.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.configureUserProcessors(i),this.options.application?.initUser&&(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,i,{username:e?.username??"User"})),i.hasAudioConfigCommands()&&(this.log("Applying audio config commands from initUser()"),i.applyAudioConfigCommands(i.flushAudioConfigCommands())),i.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let a=i.flushMacroOrders();i.applyMacroOrders(a)}if(i.hasPostProcessCommands()){this.log("Applying post-process commands from initUser()");let a=i.flushPostProcessCommands(),l=this.postProcessOrderCollector.convertCommands(a);this.applyPostProcessOrders(l)}i.needsSendSounds()&&(await this.loadSoundsFromRegistry(),i.clearSendSounds());let r=this.core.generateAllLoadPackets();r.forEach(a=>{i.recordBytesReceived(a.length)});let n=this.core.generateMacroLoadPackets(this.userId);n.forEach(a=>{i.recordBytesReceived(a.length)});let s=i.getInputBindingsLoadPacket();if(s&&(i.recordBytesReceived(s.length),this.input)){let l=i.getInputBindingRegistry().getAllTouchZones();if(l.length>0){let c=this.rendererManager.getCanvas();if(c){let h=i.getDisplays(),u=h.length>0?h[0].getSize():{x:this.options.width??80,y:this.options.height??25},m=l.map(f=>({id:f.zoneId,x:f.x,y:f.y,width:f.width,height:f.height}));this.input.enableTouchZonesDevice(c,u.x,u.y,m)?this.log(`Touch zones synchronized from registry: ${l.length} zones`):this.log("Failed to enable touch zones - mobile input may not be initialized")}}}this.log(`Simulated ${r.length} load packets, ${n.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach(({static:a,dynamic:l},c)=>{let h=this.core.getUser(c);h&&(a&&h.recordBytesReceived(a.length),l&&h.recordBytesReceived(l.length))}),this.render(),this.log("Initial render complete")}async loadSoundsFromRegistry(){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=this.core.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry into AudioManager...`);let n=this.core.getUser(this.userId),s=0;for(let o of r)try{if(o.loadType==="file"&&o.data){await e.loadFromData(o.soundId,o.name,o.data);let a=o.data.length+o.name.length+10;s+=a,this.log(`\u{1F50A} Loaded sound: ${o.name} (${o.data.length} bytes)`)}else if(o.loadType==="external"&&o.url){await e.loadFromUrl(o.soundId,o.name,o.url);let a=o.url.length+o.name.length+10;s+=a,this.log(`\u{1F50A} Loaded external sound: ${o.name} from ${o.url}`)}}catch(a){console.error(`[ClientRuntime] Failed to load sound "${o.name}":`,a)}n&&s>0&&(n.recordBytesReceived(s),this.log(`Simulated ${s} bytes for ${r.length} sound(s)`)),this.log(`\u2713 ${e.size} sounds loaded into AudioManager`)}mainLoop(e){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(e),this.render()),this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}let i=e-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&i<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}this.lastRenderTimestamp=e;let r=(e-this.lastTimestamp)/1e3,n=Math.min(r,.1);this.lastTimestamp=e,this.performanceMonitor.updateFPS(e);let s=this.core.getUser(this.userId);if(!s){this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}if(this.mode==="local"){if(this.tickRate===0){let c=performance.now();this.render();let h=performance.now()-c;this.performanceMonitor.recordFrameTiming(0,h),this.rafId=requestAnimationFrame(u=>this.mainLoop(u));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=e,this.accumulatedTime=0,this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.accumulatedTime+=n;let o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let a=0,l=0;for(;this.accumulatedTime>=o&&a<5;){let c=performance.now();if(this.collectAndApplyInput(s),this.options.application&&(this.options.application.update?.(this.core,o),this.options.application.updateUser?.(this.core,s,o)),s.clearTextInputs(),s.hasAudioConfigCommands()&&s.applyAudioConfigCommands(s.flushAudioConfigCommands()),s.hasSoundCommands()&&s.applyAudioCommands(s.flushSoundCommands()),s.hasMobileVibrationCommands()&&s.applyMobileVibrationCommands(s.flushMobileVibrationCommands()),s.hasGamepadVibrationCommands()&&s.applyGamepadVibrationCommands(s.flushGamepadVibrationCommands()),s.hasPostProcessCommands()){let m=s.flushPostProcessCommands(),v=this.postProcessOrderCollector.convertCommands(m);this.applyPostProcessOrders(v)}if(s.hasPendingMacroOrders()){let m=s.flushMacroOrders();s.applyMacroOrders(m)}let h=s.updateMacros();if(s.processMacroEvents(h),s.hasBridgeMessages()){let m=s.getBridgeMessages();this.dispatchLocalBridgeMessages(m)}this.core.endTick().forEach(({static:m,dynamic:v})=>{let f=(m?.length??0)+(v?.length??0);s.recordBytesReceived(f)}),l+=performance.now()-c,this.accumulatedTime-=o,a++}if(a>0){let c=performance.now();this.render();let h=performance.now()-c;this.performanceMonitor.recordFrameTiming(l,h)}}else{let o=s.updateMacros();s.processMacroEvents(o),this.collectAndSendInput()}this.rafId=requestAnimationFrame(o=>this.mainLoop(o))}collectAndApplyInput(e){if(!this.input)return;let i=this.rendererManager.getCanvas();if(i){let a=e.getDisplays(),c=(a.find(g=>g.getId&&g.getId()===this.primaryDisplayId)??a[0])?.getSize()??{x:this.options.width,y:this.options.height},h=c.x,u=c.y,v=this.rendererManager.getRenderer()?.getOffsets?.(),f={offsetX:v?.offsetX??0,offsetY:v?.offsetY??0};this.input.updateTouchZoneLayout(h,u,f);let C=M.InputCollector.collectMousePosition(this.input,i,h,u,f);e.setMousePosition(C.x,C.y,C.over),e.updateMacroMouse(C.x,C.y,C.isLeftDown),M.InputCollector.collectTouchPositions(this.input,i,h,u,10,f).forEach(g=>{e.setTouchPosition(g.id,g.x,g.y,g.over)});let y=this.rendererManagers.get(this.primaryDisplayId)?.getAvailableSize();y&&e.setDisplayViewport(this.primaryDisplayId,y.width,y.height)}let r=M.InputCollector.collectTextInputs(this.input);r.length>0&&e.setTextInputs(r);let n=e.getInputBindingRegistry(),s=n.getAllAxes(),o=n.getAllButtons();if(s.length>0||o.length>0){let a=M.InputCollector.collectAxisSources(s,this.input),l=M.InputCollector.collectButtonSourcesWithTransitions(o,this.input);s.forEach(c=>{let h=n.evaluateAxis(c.bindingId,a);e.setAxis(c.name,h)}),o.forEach(c=>{let h=new Map,u=new Map,m=new Map;for(let[b,p]of l)h.set(b,p.pressed),u.set(b,p.justPressed),m.set(b,p.justReleased);let v=n.evaluateButton(c.bindingId,h),f=n.evaluateButton(c.bindingId,u),C=n.evaluateButton(c.bindingId,m);e.setButton(c.name,v),e.setButton(`${c.name}_justPressed`,f),e.setButton(`${c.name}_justReleased`,C)})}this.input.poll?.()}collectAndSendInput(){!this.networkSync||!this.input||this.networkSync.sendInput(this.input)}render(){let e=this.core.getUser(this.userId);if(e){let i=e.getDisplays();for(let r of i){let n=r.getId(),s=this.rendererManagers.get(n);if(!s){console.warn(`[ClientRuntime] No renderer for display ${n}, skipping resize`);continue}let o=s.getRenderer();if(!o)continue;let a=r.getSize(),l,c,h,u,m=null;if("getGridSize"in o){let f=o,C=f.getGridSize();l=C.cols,c=C.rows,h=f.getCellWidth(),u=f.getCellHeight(),m=f.getScalingMode()}else if("getCols"in o){let f=o;l=f.getCols(),c=f.getRows(),h=f.getCellWidth(),u=f.getCellHeight(),m=f.getScalingMode()}else continue;if(m===B.ScalingMode.Responsive){let f=s.getAvailableSize(),C=256*256;"getMaxCells"in o&&(C=Math.min(C,o.getMaxCells()));let b=Math.floor(f.width/h),p=Math.floor(f.height/u);if(b=Math.min(256,Math.max(1,b)),p=Math.min(256,Math.max(1,p)),b*p>C){let y=Math.sqrt(C/(b*p));b=Math.max(1,Math.floor(b*y)),p=Math.max(1,Math.floor(p*y)),this.log(`\u26A0\uFE0F [Display ${n}] Device limit: max ${C} cells, clamping to ${b}\xD7${p}`)}(a.x!==b||a.y!==p)&&(this.log(`\u{1F4D0} [Display ${n}] Resizing display from ${a.x}\xD7${a.y} to ${b}\xD7${p} (available: ${f.width}\xD7${f.height}px, cell: ${h}\xD7${u}px)`),r.setSize(new B.Vector2(b,p)))}let v=r.getSize();(l!==v.x||c!==v.y)&&(this.log(`\u{1F4D0} [Display ${n}] Resizing renderer from ${l}\xD7${c} to ${v.x}\xD7${v.y}`),o.resize(v.x,v.y))}}super.render()}onFontChanged(e,i){let r=i??this.primaryDisplayId,n=this.postProcessOverlays.get(r)??this.postProcessOverlay;n&&"syncWithRenderer"in n&&"getCellWidth"in e&&"getCellHeight"in e&&"getCurrentScale"in e&&"getGridSize"in e&&(n.syncWithRenderer(e),this.log(`\u2713 PostProcessOverlay synced with renderer dimensions (display ${r})`))}getAudioManager(){return this.audioManager}getLayerTrafficStats(){return this.networkSync?.getLayerTrafficStats()??{}}getMiscTrafficStats(){return this.networkSync?.getMiscTrafficStats?.()??{}}getDisplayTrafficStats(){return this.networkSync?.getDisplayTrafficStats?.()??{}}getAudioContext(){return this.audioManager?.getContext()??null}getPostProcessOverlay(){return this.postProcessOverlay}applyAmbientEffectConfig(e,i){let r=this.rendererManagers.get(e)?.getRenderer();r&&"setAmbientEffect"in r&&(i.enabled?r.setAmbientEffect({blur:i.blur,scale:i.scale}):r.setAmbientEffect(!1),this.log(`[Display ${e}] Ambient effect ${i.enabled?"enabled":"disabled"}`))}applyPostProcessOrders(e){for(let i of e){let r=i.displayId??this.primaryDisplayId,n=this.postProcessOverlays.get(r)??this.postProcessOverlay,s=this.rendererManagers.get(r)?.getRenderer();switch(i.type){case w.PostProcessOrderType.SetConfig:{if(!n){console.warn(`[ClientRuntime] No PostProcessOverlay for display ${r}`);break}let o={};i.scanlines&&(o.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(o.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),n.setConfig(Object.keys(o).length>0?o:null),o.ambientEffect&&s&&this.applyAmbientEffectConfig(r,o.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case w.PostProcessOrderType.SetScanlines:{if(!n){console.warn(`[ClientRuntime] No PostProcessOverlay for display ${r}`);break}n.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case w.PostProcessOrderType.SetAmbientEffect:{this.applyAmbientEffectConfig(r,{enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case w.PostProcessOrderType.SetScalingMode:{this.applyScalingMode(r,i.mode);break}case w.PostProcessOrderType.SetGrid:{this.applyGridConfig(r,i);break}case w.PostProcessOrderType.SwitchPalette:{this.applySwitchPalette(r,i);break}case w.PostProcessOrderType.SetCellSize:{this.applyCellSize(r,i.cellWidth,i.cellHeight);break}}}}applyScalingMode(e,i){let r=this.rendererManagers.get(e)?.getRenderer();if(r&&"setScalingMode"in r){let n=(0,B.valueToScalingMode)(i);r.setScalingMode(n),this.log(`[Display ${e}] Scaling mode set to ${n}`)}}applyGridConfig(e,i){let r=this.rendererManagers.get(e)?.getRenderer();if(r&&"setGrid"in r){let n=i.colorA/255;r.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${n})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}applySwitchPalette(e,i){let r=this.core.getPaletteFromSlot(i.slotId);if(!r){this.pendingPaletteSlotByDisplay.set(e,i.slotId),console.warn(`[ClientRuntime] Palette slot ${i.slotId} not found (display ${e}); will retry when available`);return}let n=[];for(let o=0;o<256;o++){let a=r.get(o);a?n.push({r:a.r,g:a.g,b:a.b,a:a.a}):n.push({r:0,g:0,b:0,a:0})}let s=this.rendererManagers.get(e)?.getRenderer();s&&"setPalette"in s&&(s.setPalette(n),this.log(`[Display ${e}] Switched to palette slot ${i.slotId}`)),this.pendingPaletteSlotByDisplay.get(e)===i.slotId&&this.pendingPaletteSlotByDisplay.delete(e)}retryPendingPaletteSelections(){if(this.pendingPaletteSlotByDisplay.size!==0)for(let[e,i]of this.pendingPaletteSlotByDisplay.entries()){let r=this.core.getPaletteFromSlot(i);if(!r)continue;let n=[];for(let o=0;o<256;o++){let a=r.get(o);a?n.push({r:a.r,g:a.g,b:a.b,a:a.a}):n.push({r:0,g:0,b:0,a:0})}let s=this.rendererManagers.get(e)?.getRenderer();s&&"setPalette"in s&&(s.setPalette(n),this.log(`[Display ${e}] Applied pending palette slot ${i}`),this.pendingPaletteSlotByDisplay.delete(e))}}applyCellSize(e,i,r){let n=this.rendererManagers.get(e)?.getRenderer();n&&"setCellSize"in n&&(n.setCellSize(i,r),this.log(`[Display ${e}] Cell size set to ${i}\xD7${r}px`))}patternFromType(e){switch(e){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}dispatchLocalBridgeMessages(e){for(let i of e){let{channel:r,data:n}=i,s=this.localBridgeHandlers.get(r);if(s)for(let o of s)try{o(n)}catch(a){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,a)}for(let o of this.localBridgeWildcardHandlers)try{o(r,n)}catch(a){console.error("[ClientRuntime] Bridge wildcard handler error:",a)}this.log(`Bridge message dispatched on channel '${r}'`)}}sendBridge(e,i){if(this.mode==="connected"&&this.networkSync)this.networkSync.sendBridge(e,i);else if(this.mode==="local"){let r=this.core.getUser(this.userId);r&&this.options.application?.onBridgeMessage&&(this.options.application.onBridgeMessage(this.core,r,e,i),this.log(`Bridge message sent to application on channel '${e}'`))}}onBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.onBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.add(i):(this.localBridgeHandlers.has(e)||this.localBridgeHandlers.set(e,new Set),this.localBridgeHandlers.get(e).add(i)),this.log(`Bridge handler registered for channel '${e}'`))}offBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.offBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.delete(i):this.localBridgeHandlers.get(e)?.delete(i))}removeAllBridgeHandlers(e){this.mode==="connected"&&this.networkSync?this.networkSync.removeAllBridgeHandlers(e):this.mode==="local"&&(e===void 0?(this.localBridgeHandlers.clear(),this.localBridgeWildcardHandlers.clear()):e==="*"?this.localBridgeWildcardHandlers.clear():this.localBridgeHandlers.delete(e))}log(e){this.options.debug&&console.warn(`[ClientRuntime/${this.mode}] ${e}`)}};R(J,"ClientRuntime");var z=J;var T=require("@utsp/input");var X=class X{constructor(t={}){d(this,"input");d(this,"mobileVibration");this.input=new T.UnifiedInputRouter({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:t.debug,keyboardConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mouseConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mobileConfig:{preventDefault:t.mobileInputConfig?.preventDefault??!1,passive:t.mobileInputConfig?.passive??!0,maxTouches:t.mobileInputConfig?.maxTouches??10}}),this.mobileVibration=new T.MobileVibration({debug:t.debug})}setMobileTarget(t){this.input.setMobileTarget(t)}start(){this.input.start()}stop(){this.input.stop()}destroy(){this.input.destroy()}getRouter(){return this.input}getGamepad(){return this.input.getGamepad()}getMobileVibration(){return this.mobileVibration}collectAndApply(t,e,i,r,n){if(e){let c=T.InputCollector.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(c.x,c.y,c.over),t.updateMacroMouse(c.x,c.y,c.isLeftDown),T.InputCollector.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let s=T.InputCollector.collectTextInputs(this.input);s.length>0&&t.setTextInputs(s);let o=t.getInputBindingRegistry(),a=o.getAllAxes(),l=o.getAllButtons();if(a.length>0||l.length>0){let c=T.InputCollector.collectAxisSources(a,this.input),h=T.InputCollector.collectButtonSourcesWithTransitions(l,this.input);a.forEach(u=>{let m=o.evaluateAxis(u.bindingId,c);t.setAxis(u.name,m)}),l.forEach(u=>{let m=new Map,v=new Map,f=new Map;for(let[y,g]of h)m.set(y,g.pressed),v.set(y,g.justPressed),f.set(y,g.justReleased);let C=o.evaluateButton(u.bindingId,m),b=o.evaluateButton(u.bindingId,v),p=o.evaluateButton(u.bindingId,f);t.setButton(u.name,C),t.setButton(`${u.name}_justPressed`,b),t.setButton(`${u.name}_justReleased`,p)})}this.input.poll?.()}processVibrationCommands(t){t.hasMobileVibrationCommands()&&t.applyMobileVibrationCommands(t.flushMobileVibrationCommands()),t.hasGamepadVibrationCommands()&&t.applyGamepadVibrationCommands(t.flushGamepadVibrationCommands())}};R(X,"InputFeature");var W=X;var ue=require("@utsp/audio");var _=class _{constructor(t={}){d(this,"audioManager");d(this,"debug");this.debug=t.debug??!1,this.audioManager=new ue.AudioManager({debug:t.debug})}initialize(){this.audioManager.initialize()}playStartSound(){this.audioManager.playStartSound()}stopAll(){this.audioManager.stopAll()}destroy(){this.audioManager.destroy()}getManager(){return this.audioManager}getContext(){return this.audioManager.getContext()}injectIntoUser(t){t.setAudioProcessor(this.audioManager)}processAudioCommands(t){t.hasAudioConfigCommands()&&t.applyAudioConfigCommands(t.flushAudioConfigCommands()),t.hasSoundCommands()&&t.applyAudioCommands(t.flushSoundCommands())}async loadSoundsFromRegistry(t){let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=t.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry...`);for(let n of r)try{n.loadType==="file"&&n.data?(await e.loadFromData(n.soundId,n.name,n.data),this.log(`Loaded sound: ${n.name} (${n.data.length} bytes)`)):n.loadType==="external"&&n.url&&(await e.loadFromUrl(n.soundId,n.name,n.url),this.log(`Loaded external sound: ${n.name} from ${n.url}`))}catch(s){console.error(`[AudioFeature] Failed to load sound "${n.name}":`,s)}this.log(`${e.size} sounds loaded`)}log(t){this.debug&&console.warn(`[AudioFeature] ${t}`)}};R(_,"AudioFeature");var H=_;var k=require("@utsp/core"),he=require("@utsp/render"),pe=require("@utsp/types");var ee=class ee{constructor(t,e={}){d(this,"overlay");d(this,"orderCollector");d(this,"debug");this.debug=e.debug??!1,this.overlay=new he.PostProcessOverlay(t),this.orderCollector=new k.PostProcessOrderCollector}destroy(){this.overlay.destroy()}getOverlay(){return this.overlay}syncWithRenderer(t){this.overlay.syncWithRenderer(t),this.log("Overlay synced with renderer dimensions")}processCommands(t,e){if(!t.hasPostProcessCommands())return;let i=t.flushPostProcessCommands(),r=this.orderCollector.convertCommands(i);this.applyOrders(r,e)}applyOrders(t,e){for(let i of t){let r=i.displayId;switch(i.type){case k.PostProcessOrderType.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.overlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffect(e,n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case k.PostProcessOrderType.SetScanlines:{this.overlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case k.PostProcessOrderType.SetAmbientEffect:{this.applyAmbientEffect(e,{enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case k.PostProcessOrderType.SetScalingMode:{this.applyScalingMode(e,r,i.mode);break}case k.PostProcessOrderType.SetGrid:{this.applyGridConfig(e,r,i);break}}}}applyAmbientEffect(t,e){t&&"setAmbientEffect"in t&&(e.enabled?t.setAmbientEffect({blur:e.blur,scale:e.scale}):t.setAmbientEffect(!1))}applyScalingMode(t,e,i){if(t&&"setScalingMode"in t){let r=(0,pe.valueToScalingMode)(i);t.setScalingMode(r),this.log(`[Display ${e}] Scaling mode set to ${r}`)}}applyGridConfig(t,e,i){if(t&&"setGrid"in t){let r=i.colorA/255;t.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${r})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}patternFromType(t){switch(t){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}log(t){this.debug&&console.warn(`[PostProcessFeature] ${t}`)}};R(ee,"PostProcessFeature");var Q=ee;var ge=require("@utsp/render"),me=require("@utsp/audio");
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Core, User, AnyPostProcessOrder } from '@utsp/core';
|
|
1
|
+
import { Layer, Core, User, AnyPostProcessOrder } from '@utsp/core';
|
|
2
2
|
import { IApplication, IRenderer } from '@utsp/types';
|
|
3
3
|
export { IApplication } from '@utsp/types';
|
|
4
4
|
import { AutoplayOverlayOptions, PostProcessOverlay } from '@utsp/render';
|
|
@@ -39,6 +39,11 @@ interface BaseRuntimeOptions {
|
|
|
39
39
|
application?: IApplication;
|
|
40
40
|
/** Container HTML element for rendering */
|
|
41
41
|
container: HTMLElement;
|
|
42
|
+
/** Optional per-display containers (enables multi-display rendering) */
|
|
43
|
+
displayContainers?: Array<{
|
|
44
|
+
id: number;
|
|
45
|
+
container: HTMLElement;
|
|
46
|
+
}>;
|
|
42
47
|
/** Enable debug logging */
|
|
43
48
|
debug?: boolean;
|
|
44
49
|
/** Initial display width in columns (default: 80). Can be overridden by IApplication via Display */
|
|
@@ -381,6 +386,7 @@ type ConcreteRenderer = IRenderer & {
|
|
|
381
386
|
declare class RendererManager {
|
|
382
387
|
private renderer;
|
|
383
388
|
private options;
|
|
389
|
+
private layerFilter;
|
|
384
390
|
/**
|
|
385
391
|
* Creates a new RendererManager
|
|
386
392
|
*
|
|
@@ -394,6 +400,14 @@ declare class RendererManager {
|
|
|
394
400
|
* ```
|
|
395
401
|
*/
|
|
396
402
|
constructor(renderer: ConcreteRenderer, options?: RendererManagerOptions);
|
|
403
|
+
/**
|
|
404
|
+
* Restrict rendering to layers accepted by the filter
|
|
405
|
+
*/
|
|
406
|
+
setLayerFilter(filter: ((layer: Layer) => boolean) | null): void;
|
|
407
|
+
/**
|
|
408
|
+
* Get the currently configured layer filter (if any).
|
|
409
|
+
*/
|
|
410
|
+
getLayerFilter(): ((layer: Layer) => boolean) | null;
|
|
397
411
|
/**
|
|
398
412
|
* Initialize renderer (wait for ready + load default font)
|
|
399
413
|
*
|
|
@@ -450,6 +464,11 @@ declare class RendererManager {
|
|
|
450
464
|
* }
|
|
451
465
|
* ```
|
|
452
466
|
*/
|
|
467
|
+
renderDisplay(display: any): boolean;
|
|
468
|
+
/**
|
|
469
|
+
* Compatibility helper: render the first display from core/userId.
|
|
470
|
+
* Multi-display callers should prefer renderDisplay().
|
|
471
|
+
*/
|
|
453
472
|
render(core: Core, userId: string): boolean;
|
|
454
473
|
/**
|
|
455
474
|
* Get the underlying renderer instance
|
|
@@ -551,12 +570,23 @@ declare class RendererManager {
|
|
|
551
570
|
declare abstract class BaseClientRuntime {
|
|
552
571
|
/** Core instance managing application state */
|
|
553
572
|
protected core: Core;
|
|
554
|
-
/** Renderer
|
|
573
|
+
/** Renderer managers per display */
|
|
574
|
+
protected rendererManagers: Map<number, RendererManager>;
|
|
575
|
+
/** Promises for renderer initialization per display */
|
|
576
|
+
private rendererInitPromises;
|
|
577
|
+
/** Primary renderer manager (displayId = primaryDisplayId) for legacy access */
|
|
555
578
|
protected rendererManager: RendererManager;
|
|
556
|
-
/** Current renderer type */
|
|
579
|
+
/** Current renderer type (primary) */
|
|
557
580
|
protected rendererType: RendererType;
|
|
581
|
+
/** Primary display id (used for input and defaults) */
|
|
582
|
+
protected primaryDisplayId: number;
|
|
583
|
+
/** Display configuration map */
|
|
584
|
+
protected displayConfigs: Map<number, {
|
|
585
|
+
container: HTMLElement;
|
|
586
|
+
renderer?: RendererType;
|
|
587
|
+
}>;
|
|
558
588
|
/** Normalized options with defaults applied (application remains optional for connected mode) */
|
|
559
|
-
protected options: Omit<Required<BaseRuntimeOptions>, 'application'> & Pick<BaseRuntimeOptions, 'application'>;
|
|
589
|
+
protected options: Omit<Required<BaseRuntimeOptions>, 'application' | 'displayContainers'> & Pick<BaseRuntimeOptions, 'application' | 'displayContainers'>;
|
|
560
590
|
/** Is the runtime currently running */
|
|
561
591
|
protected running: boolean;
|
|
562
592
|
/** Timestamp when runtime started */
|
|
@@ -589,7 +619,17 @@ declare abstract class BaseClientRuntime {
|
|
|
589
619
|
/**
|
|
590
620
|
* Create the appropriate renderer based on options
|
|
591
621
|
*/
|
|
592
|
-
protected createRenderer(): ConcreteRenderer;
|
|
622
|
+
protected createRenderer(container: HTMLElement, rendererType: RendererType): ConcreteRenderer;
|
|
623
|
+
/**
|
|
624
|
+
* Create renderer managers for all configured displays.
|
|
625
|
+
* Rule: if total displays >= 3, force Terminal2D for all (browser GL context limits).
|
|
626
|
+
*/
|
|
627
|
+
protected createRendererManagers(): void;
|
|
628
|
+
/**
|
|
629
|
+
* Ensure a renderer manager exists for the given display.
|
|
630
|
+
* Creates a hidden container and renderer if missing (used for debug displays like 255).
|
|
631
|
+
*/
|
|
632
|
+
protected ensureRendererManager(displayId: number): RendererManager | undefined;
|
|
593
633
|
/**
|
|
594
634
|
* Get the renderer type
|
|
595
635
|
*/
|
|
@@ -696,16 +736,11 @@ declare abstract class BaseClientRuntime {
|
|
|
696
736
|
* Create a local user
|
|
697
737
|
*/
|
|
698
738
|
protected createLocalUser(): Promise<void>;
|
|
739
|
+
protected processInitialOrders(user: User): void;
|
|
699
740
|
/**
|
|
700
|
-
* Hook
|
|
701
|
-
* Override to inject audio processor, vibration, etc.
|
|
741
|
+
* Hook for subclasses to configure user right after creation.
|
|
702
742
|
*/
|
|
703
743
|
protected onUserCreated(_user: User): void;
|
|
704
|
-
/**
|
|
705
|
-
* Process initial orders after initUser()
|
|
706
|
-
* Override to handle macro orders, post-process, etc.
|
|
707
|
-
*/
|
|
708
|
-
protected processInitialOrders(user: User): void;
|
|
709
744
|
/**
|
|
710
745
|
* Main game loop
|
|
711
746
|
*/
|
|
@@ -746,7 +781,7 @@ declare abstract class BaseClientRuntime {
|
|
|
746
781
|
* Hook called after font change for post-process sync
|
|
747
782
|
* Override in subclasses with post-process support
|
|
748
783
|
*/
|
|
749
|
-
protected onFontChanged(_renderer: ConcreteRenderer): void;
|
|
784
|
+
protected onFontChanged(_renderer: ConcreteRenderer, _displayId?: number): void;
|
|
750
785
|
/**
|
|
751
786
|
* Debug logging
|
|
752
787
|
*/
|
|
@@ -758,7 +793,17 @@ declare abstract class BaseClientRuntime {
|
|
|
758
793
|
/**
|
|
759
794
|
* Get the renderer manager
|
|
760
795
|
*/
|
|
761
|
-
getRendererManager(): RendererManager;
|
|
796
|
+
getRendererManager(displayId?: number): RendererManager;
|
|
797
|
+
/**
|
|
798
|
+
* Configure/override per-display containers.
|
|
799
|
+
*
|
|
800
|
+
* When provided, the runtime will render one independent renderer per display.
|
|
801
|
+
* This keeps rendering DRY: each renderer receives palette/font updates via Core listeners.
|
|
802
|
+
*/
|
|
803
|
+
setDisplayContainers(containers: Array<{
|
|
804
|
+
id: number;
|
|
805
|
+
container: HTMLElement;
|
|
806
|
+
}>): void;
|
|
762
807
|
/**
|
|
763
808
|
* Get current user ID
|
|
764
809
|
*/
|
|
@@ -781,6 +826,18 @@ interface BaseClientRuntimeOptions {
|
|
|
781
826
|
application: IApplication;
|
|
782
827
|
/** Container HTML element for rendering */
|
|
783
828
|
container: HTMLElement;
|
|
829
|
+
/**
|
|
830
|
+
* Optional per-display containers.
|
|
831
|
+
*
|
|
832
|
+
* When provided, the runtime can render multiple displays at once.
|
|
833
|
+
* Each entry defines which parent element hosts the renderer for that display.
|
|
834
|
+
*
|
|
835
|
+
* If omitted, the runtime keeps legacy single-display behavior (display 0 into `container`).
|
|
836
|
+
*/
|
|
837
|
+
displayContainers?: Array<{
|
|
838
|
+
id: number;
|
|
839
|
+
container: HTMLElement;
|
|
840
|
+
}>;
|
|
784
841
|
/** Enable debug logging */
|
|
785
842
|
debug?: boolean;
|
|
786
843
|
/** Initial display width in columns (default: 80). Can be overridden by IApplication via Display */
|
|
@@ -944,10 +1001,13 @@ declare class ClientRuntime extends BaseClientRuntime {
|
|
|
944
1001
|
private audioManager;
|
|
945
1002
|
private mobileVibration;
|
|
946
1003
|
private postProcessOverlay;
|
|
1004
|
+
private postProcessOverlays;
|
|
947
1005
|
private postProcessOrderCollector;
|
|
1006
|
+
private pendingPaletteSlotByDisplay;
|
|
948
1007
|
private localBridgeHandlers;
|
|
949
1008
|
private localBridgeWildcardHandlers;
|
|
950
1009
|
constructor(options: ClientRuntimeOptions);
|
|
1010
|
+
private rebuildPostProcessOverlays;
|
|
951
1011
|
getMode(): ClientRuntimeMode;
|
|
952
1012
|
setTickRate(tickRate: number): void;
|
|
953
1013
|
/**
|
|
@@ -1043,11 +1103,26 @@ declare class ClientRuntime extends BaseClientRuntime {
|
|
|
1043
1103
|
/**
|
|
1044
1104
|
* Override onFontChanged to sync PostProcessOverlay
|
|
1045
1105
|
*/
|
|
1046
|
-
protected onFontChanged(renderer: ConcreteRenderer): void;
|
|
1106
|
+
protected onFontChanged(renderer: ConcreteRenderer, displayId?: number): void;
|
|
1047
1107
|
/**
|
|
1048
1108
|
* Get the AudioManager instance
|
|
1049
1109
|
*/
|
|
1050
1110
|
getAudioManager(): InstanceType<typeof AudioManager> | null;
|
|
1111
|
+
/** Expose per-layer traffic stats from NetworkSync (if available). */
|
|
1112
|
+
getLayerTrafficStats(): Record<number, {
|
|
1113
|
+
total: number;
|
|
1114
|
+
bytesPerSec1s: number;
|
|
1115
|
+
}>;
|
|
1116
|
+
/** Expose misc UpdatePacket traffic stats from NetworkSync (if available). */
|
|
1117
|
+
getMiscTrafficStats(): Record<string, {
|
|
1118
|
+
total: number;
|
|
1119
|
+
bytesPerSec1s: number;
|
|
1120
|
+
}>;
|
|
1121
|
+
/** Expose per-display traffic stats from NetworkSync (if available). */
|
|
1122
|
+
getDisplayTrafficStats(): Record<number, {
|
|
1123
|
+
total: number;
|
|
1124
|
+
bytesPerSec1s: number;
|
|
1125
|
+
}>;
|
|
1051
1126
|
/**
|
|
1052
1127
|
* Get the AudioContext instance
|
|
1053
1128
|
*/
|
|
@@ -1063,14 +1138,14 @@ declare class ClientRuntime extends BaseClientRuntime {
|
|
|
1063
1138
|
/**
|
|
1064
1139
|
* Apply post-process orders
|
|
1065
1140
|
*
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1068
|
-
* A future multi-display implementation would route orders to the correct renderer.
|
|
1141
|
+
* Each order contains a displayId indicating which display it targets.
|
|
1142
|
+
* Orders are routed to the matching renderer/overlay; missing targets are skipped with a warning.
|
|
1069
1143
|
*/
|
|
1070
1144
|
private applyPostProcessOrders;
|
|
1071
1145
|
private applyScalingMode;
|
|
1072
1146
|
private applyGridConfig;
|
|
1073
1147
|
private applySwitchPalette;
|
|
1148
|
+
private retryPendingPaletteSelections;
|
|
1074
1149
|
private applyCellSize;
|
|
1075
1150
|
private patternFromType;
|
|
1076
1151
|
private dispatchLocalBridgeMessages;
|
|
@@ -1244,5 +1319,30 @@ declare class PostProcessFeature {
|
|
|
1244
1319
|
private log;
|
|
1245
1320
|
}
|
|
1246
1321
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1322
|
+
/**
|
|
1323
|
+
* Default atlas configuration
|
|
1324
|
+
*/
|
|
1325
|
+
declare const DEFAULT_ATLAS_CONFIG: {
|
|
1326
|
+
/** Glyph width in pixels */
|
|
1327
|
+
glyphWidth: number;
|
|
1328
|
+
/** Glyph height in pixels */
|
|
1329
|
+
glyphHeight: number;
|
|
1330
|
+
/** Cell width in pixels (same as glyph for this font) */
|
|
1331
|
+
cellWidth: number;
|
|
1332
|
+
/** Cell height in pixels (same as glyph for this font) */
|
|
1333
|
+
cellHeight: number;
|
|
1334
|
+
/** Number of 256-char blocks in the atlas */
|
|
1335
|
+
atlasBlocks: 1;
|
|
1336
|
+
/** Atlas width in pixels */
|
|
1337
|
+
atlasWidth: number;
|
|
1338
|
+
/** Atlas height in pixels */
|
|
1339
|
+
atlasHeight: number;
|
|
1340
|
+
};
|
|
1341
|
+
/**
|
|
1342
|
+
* Decode base64 PNG data to Uint8Array
|
|
1343
|
+
* Note: This is a browser-only package, so we use atob (available in all modern browsers)
|
|
1344
|
+
*/
|
|
1345
|
+
declare function decodeDefaultAtlas(): Uint8Array;
|
|
1346
|
+
|
|
1347
|
+
export { AudioFeature, BaseClientRuntime, ClientRuntime, DEFAULT_ATLAS_CONFIG, InputFeature, PostProcessFeature, RendererManager, RendererType, decodeDefaultAtlas };
|
|
1348
|
+
export type { AudioFeatureOptions, BaseClientRuntimeOptions, BaseRuntimeOptions, BaseRuntimeStats, ClientRuntimeMode, ClientRuntimeOptions, ClientRuntimeStats, ConcreteRenderer, ConnectedModeOptions, FrameTiming$1 as FrameTiming, InputFeatureOptions, LocalModeOptions, PostProcessFeatureOptions, RenderMode, RendererManagerOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var X=Object.defineProperty;var ie=(R,t,e)=>t in R?X(R,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):R[t]=e;var y=(R,t)=>X(R,"name",{value:t,configurable:!0});var a=(R,t,e)=>(ie(R,typeof t!="symbol"?t+"":t,e),e);import{Core as ne}from"@utsp/core";import{TerminalGL as oe,Terminal2D as se}from"@utsp/render";var _=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(_||{});var O=class O{constructor(t=60){a(this,"frameCount",0);a(this,"fps",0);a(this,"fpsUpdateTime",0);a(this,"lastCoreTime",0);a(this,"lastRenderTime",0);a(this,"lastTotalTime",0);a(this,"coreTimeSamples",[]);a(this,"renderTimeSamples",[]);a(this,"totalTimeSamples",[]);a(this,"maxSamples");if(t<=0)throw new Error("maxSamples must be positive");this.maxSamples=t}startTracking(t){this.fpsUpdateTime=t,this.frameCount=0,this.fps=0}updateFPS(t){this.frameCount++,t-this.fpsUpdateTime>=1e3&&(this.fps=Math.round(this.frameCount*1e3/(t-this.fpsUpdateTime)),this.frameCount=0,this.fpsUpdateTime=t)}recordFrameTiming(t,e){let i=t+e;this.lastCoreTime=t,this.lastRenderTime=e,this.lastTotalTime=i,this.coreTimeSamples.push(t),this.renderTimeSamples.push(e),this.totalTimeSamples.push(i),this.coreTimeSamples.length>this.maxSamples&&(this.coreTimeSamples.shift(),this.renderTimeSamples.shift(),this.totalTimeSamples.shift())}getFPS(){return this.fps}getLastFrameTiming(){if(!(this.lastCoreTime===0&&this.lastRenderTime===0))return{coreTime:this.lastCoreTime,renderTime:this.lastRenderTime,totalTime:this.lastTotalTime}}getAverageFrameTiming(){if(this.coreTimeSamples.length!==0)return{coreTime:this.average(this.coreTimeSamples),renderTime:this.average(this.renderTimeSamples),totalTime:this.average(this.totalTimeSamples)}}getStats(){return{fps:this.fps,lastFrame:this.getLastFrameTiming(),avgFrame:this.getAverageFrameTiming(),sampleCount:this.coreTimeSamples.length,maxSamples:this.maxSamples}}reset(){this.frameCount=0,this.fps=0,this.fpsUpdateTime=0,this.lastCoreTime=0,this.lastRenderTime=0,this.lastTotalTime=0,this.coreTimeSamples=[],this.renderTimeSamples=[],this.totalTimeSamples=[]}average(t){return t.length===0?0:t.reduce((e,i)=>e+i,0)/t.length}};y(O,"PerformanceMonitor");var E=O;var re="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",M={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function ee(){let R=atob(re),t=new Uint8Array(R.length);for(let e=0;e<R.length;e++)t[e]=R.charCodeAt(e);return t}y(ee,"decodeDefaultAtlas");var L=class L{constructor(t,e={}){a(this,"renderer");a(this,"options");this.renderer=t,this.options={debug:e.debug??!1,useImageDataRendering:e.useImageDataRendering??!1}}async initialize(t){if("setImageDataRendering"in this.renderer&&this.options.useImageDataRendering&&(this.renderer.setImageDataRendering(!0),this.log("ImageData rendering enabled")),"setImageFont"in this.renderer&&typeof this.renderer.setImageFont=="function"){this.log("Loading default 8x8 font atlas...");let e=ee();await this.renderer.setImageFont(e,M.glyphWidth,M.glyphHeight,M.cellWidth,M.cellHeight,M.atlasBlocks),this.log("Default 8x8 font atlas loaded")}await this.waitForReady(),this.log("Initialized and ready")}async waitForReady(t=100,e=50){this.log("Waiting for renderer to be ready");let i=0;return new Promise((r,n)=>{let o=y(()=>{if(i++,this.renderer.isReady())this.log(`Renderer ready after ${i*e}ms`),r();else if(i>=t){let d=`Renderer failed to be ready after ${t*e}ms`;this.log(d),n(new Error(d))}else setTimeout(o,e)},"check");o()})}async loadDefaultFont(t){console.warn("[RendererManager] loadDefaultFont() is deprecated. Use ImageFont instead.")}render(t,e){if(!this.renderer.isReady())return console.warn("[RENDERER MANAGER] Renderer not ready"),!1;let i=t.getRenderState(e);if(!i||i.displays.length===0)return console.warn("[RENDERER MANAGER] No render state or no displays"),!1;let r=i.displays[0];return this.renderer.renderDisplayData(r),!0}getRenderer(){return this.renderer}getCanvas(){return this.renderer.getCanvas()}getAvailableSize(){if(this.renderer.getAvailableSize)return this.renderer.getAvailableSize();let t=this.getCanvas();return{width:t?.clientWidth||t?.width||0,height:t?.clientHeight||t?.height||0}}isReady(){return this.renderer.isReady()}destroy(){this.log("Destroying renderer"),this.renderer.destroy()}log(t){this.options.debug&&console.warn(`[RendererManager] ${t}`)}};y(L,"RendererManager");var x=L;var $=class ${constructor(t){a(this,"core");a(this,"rendererManager");a(this,"rendererType");a(this,"options");a(this,"running",!1);a(this,"startTime",0);a(this,"lastTimestamp",0);a(this,"userId","");a(this,"visibilityChangeHandler");a(this,"tickRate",30);a(this,"accumulatedTime",0);a(this,"FRAME_TIME_MIN",1e3/60);a(this,"lastRenderTimestamp",0);a(this,"rafId",0);a(this,"firstTickDone",!1);a(this,"performanceMonitor");a(this,"renderMode","continuous");a(this,"renderRequested",!1);this.options={application:t.application,container:t.container,debug:t.debug??!1,width:t.width??80,height:t.height??25,renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,renderMode:t.renderMode??"continuous"},this.log("Initializing BaseClientRuntime"),this.core=new ne({mode:"client",maxUsers:1}),this.core.onPaletteChanged(i=>{this.onCorePaletteChanged(i)}),this.core.onBitmapFontChanged(i=>{this.onCoreBitmapFontChanged(i)}),this.core.onImageFontChanged(i=>{this.onCoreImageFontChanged(i)}),this.visibilityChangeHandler=()=>{!document.hidden&&this.running&&(this.lastTimestamp=performance.now(),this.accumulatedTime=0,this.log("Tab visible: Reset timing"))},document.addEventListener("visibilitychange",this.visibilityChangeHandler);let e=this.createRenderer();this.rendererType=this.options.renderer,this.rendererManager=new x(e,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering}),this.performanceMonitor=new E(60),this.tickRate=20,this.renderMode=t.renderMode??"continuous",this.log(`Configured: tickRate=${this.tickRate}, renderMode=${this.renderMode}`),this.log("BaseClientRuntime initialized")}createRenderer(){if((this.options.renderer??"webgl")==="terminal2d"){this.log("Creating Terminal 2D renderer");let n=new se(this.options.container,{fixedCols:this.options.width,fixedRows:this.options.height,cellAspectRatio:1});return this.options.useImageDataRendering&&(this.log("Enabling ImageData rendering (pixel-perfect, optimized)"),n.setImageDataRendering(!0)),this.log("Canvas 2D renderer created successfully"),n}this.log("Creating TerminalGL renderer");let i=document.createElement("canvas").getContext("webgl");if(!i)throw new Error("WebGL not supported. TerminalGL requires WebGL 1.0.");if(!i.getExtension("OES_element_index_uint"))throw new Error("OES_element_index_uint extension not supported. TerminalGL requires this extension for large terminals (256x256).");if(!(this.options.container instanceof HTMLDivElement))throw new Error(`TerminalGL requires container to be an HTMLDivElement. Received: ${this.options.container.tagName}`);let r=new oe(this.options.container,{cols:this.options.width,rows:this.options.height,charWidth:8,charHeight:8});return this.log("TerminalGL created successfully"),r}getRendererType(){return this.rendererType}isRunning(){return this.running}setTickRate(t){if(t<0||t>1e3)throw new Error(`Invalid tick rate: ${t}. Must be between 0 and 1000.`);if(this.tickRate=t,this.userId){let e=this.core.getUser(this.userId);e&&e.setBytesTickRate(t>0?t:20)}t===0&&this.renderMode==="continuous"?(this.renderMode="on-demand",this.log("Tick rate set to 0, switched to on-demand render mode")):this.log(`Tick rate set to ${t} TPS`)}setRenderMode(t){this.renderMode=t,this.log(`Render mode set to ${t}`)}setMaxFPS(t){if(t<=0||t>240)throw new Error(`Invalid FPS: ${t}. Must be between 1 and 240.`);this.FRAME_TIME_MIN=1e3/t,this.log(`Max FPS set to ${t}`)}getTickRate(){return this.tickRate}requestRender(){this.renderMode==="on-demand"&&(this.renderRequested=!0,this.log("Render requested"))}async start(){if(this.running){this.log("Already running");return}this.log("Starting BaseClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.options.application&&(this.log("Calling application.init()"),await this.options.application.init(this.core,this)),await this.onAfterInit(),await this.createLocalUser(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onBeforeStart(){}async onAfterInit(){}async stop(){if(!this.running)return;this.log("Stopping BaseClientRuntime"),this.running=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),await this.onStop();let t=this.core.getUser(this.userId);t&&this.options.application?.destroyUser&&this.options.application.destroyUser(this.core,t,"Runtime stopped"),this.log("BaseClientRuntime stopped")}async onStop(){}getStats(){let t=this.performanceMonitor.getStats();return{running:this.running,userCount:this.core.getUsers().length,fps:t.fps,uptime:this.running?performance.now()-this.startTime:0,totalFrames:0,lastFrameTiming:t.lastFrame,avgFrameTiming:t.avgFrame}}getTotalBytesReceived(){return this.core.getUser(this.userId)?.getTotalBytesReceived()??0}getBytesReceivedPerSecond(){return this.core.getUser(this.userId)?.getBytesReceivedPerSecond()??0}resetBytesReceived(){let t=this.core.getUser(this.userId);t&&t.resetByteCounters()}async destroy(){await this.stop(),this.log("Destroying BaseClientRuntime"),this.visibilityChangeHandler&&(document.removeEventListener("visibilitychange",this.visibilityChangeHandler),this.visibilityChangeHandler=void 0),await this.onDestroy();let t=this.rendererManager.getRenderer();t&&"clearOnResizeCallback"in t&&t.clearOnResizeCallback(),this.options.application?.destroy&&this.options.application.destroy(),this.rendererManager.destroy(),this.log("BaseClientRuntime destroyed")}async onDestroy(){}async createLocalUser(){this.userId="local",this.log(`Creating local user: ${this.userId}`);let t=this.core.createUser(this.userId,"User");t.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.onUserCreated(t),this.options.application?.initUser&&(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,t,{username:"User"})),this.processInitialOrders(t);let e=this.core.generateAllLoadPackets();e.forEach(o=>{t.recordBytesReceived(o.length)});let i=this.core.generateMacroLoadPackets(this.userId);i.forEach(o=>{t.recordBytesReceived(o.length)});let r=t.getInputBindingsLoadPacket();r&&t.recordBytesReceived(r.length),this.log(`Simulated ${e.length} load packets, ${i.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach(({static:o,dynamic:s},d)=>{let l=this.core.getUser(d);l&&(o&&l.recordBytesReceived(o.length),s&&l.recordBytesReceived(s.length))}),this.render(),this.log("Initial render complete")}onUserCreated(t){}processInitialOrders(t){if(t.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let e=t.flushMacroOrders();t.applyMacroOrders(e)}}mainLoop(t){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(t),this.render()),this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}let e=t-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&e<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.lastRenderTimestamp=t;let i=(t-this.lastTimestamp)/1e3,r=Math.min(i,.1);this.lastTimestamp=t,this.performanceMonitor.updateFPS(t);let n=this.core.getUser(this.userId);if(!n){this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}if(this.tickRate===0){let l=performance.now();this.render();let c=performance.now()-l;this.performanceMonitor.recordFrameTiming(0,c),this.rafId=requestAnimationFrame(p=>this.mainLoop(p));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=t,this.accumulatedTime=0,this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.accumulatedTime+=r;let o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let s=0,d=0;for(;this.accumulatedTime>=o&&s<5;){let l=performance.now();this.collectAndApplyInput(n),this.options.application&&(this.options.application.update?.(this.core,o),this.options.application.updateUser?.(this.core,n,o)),this.processTickOrders(n);let c=n.updateMacros();n.processMacroEvents(c),this.core.endTick().forEach(({static:u,dynamic:h})=>{let m=(u?.length??0)+(h?.length??0);n.recordBytesReceived(m)}),d+=performance.now()-l,this.accumulatedTime-=o,s++}if(s>0){let l=performance.now();this.render();let c=performance.now()-l;this.performanceMonitor.recordFrameTiming(d,c)}this.rafId=requestAnimationFrame(l=>this.mainLoop(l))}collectAndApplyInput(t){}processTickOrders(t){if(t.clearTextInputs(),t.hasPendingMacroOrders()){let e=t.flushMacroOrders();t.applyMacroOrders(e)}}render(){let t=this.core.getUser(this.userId);if(t){let e=t.getMacroRenderOrders();for(let[i,r]of e){let n=t.getLayerById(i);n&&r.length>0&&n.addTemporaryOrders(r)}}this.rendererManager.render(this.core,this.userId)}onCorePaletteChanged(t){this.log(`Palette changed, updating renderer (${t.size} colors)`);let e=[];for(let r=0;r<256;r++){let n=t.get(r);n?e.push({r:n.r,g:n.g,b:n.b,a:n.a}):e.push({r:0,g:0,b:0,a:0})}let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update palette: renderer is null");return}"setPalette"in i&&typeof i.setPalette=="function"&&(i.setPalette(e),this.log("\u2713 Renderer palette updated successfully"))}onCoreBitmapFontChanged(t){this.log(`Bitmap font ${t} changed, updating renderer`);let e=this.core.getBitmapFont(t);if(!e){console.warn(`[BaseClientRuntime] Font ${t} not found in registry`);return}let i=new Map,r=0;for(let o=0;o<256;o++){let s=e.getGlyph(o);s&&(i.set(o,s),r++)}this.log(`Built bitmap font map with ${r} glyphs, dimensions: ${e.getCharWidth()}x${e.getCharHeight()}`);let n=this.rendererManager.getRenderer();if(!n){console.warn("[BaseClientRuntime] Cannot update font: renderer is null");return}"setBitmapFont"in n&&typeof n.setBitmapFont=="function"&&(n.setBitmapFont(i,e.getCharWidth(),e.getCharHeight(),e.getCellWidth(),e.getCellHeight()),this.log("\u2713 Renderer bitmap font updated successfully"),this.onFontChanged(n))}onCoreImageFontChanged(t){this.log(`Image font ${t} changed, updating renderer`);let e=this.core.getImageFont(t);if(!e){console.warn(`[BaseClientRuntime] Image font ${t} not found in registry`);return}this.log(`Loading image font with ${e.getAtlasBlocks()} blocks, glyph: ${e.getGlyphWidth()}x${e.getGlyphHeight()}`);let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update image font: renderer is null");return}"setImageFont"in i&&typeof i.setImageFont=="function"&&(i.setImageFont(e.getImageData(),e.getGlyphWidth(),e.getGlyphHeight(),e.getCellWidth(),e.getCellHeight(),e.getAtlasBlocks()),this.log("\u2713 Renderer image font updated successfully"),this.onFontChanged(i))}onFontChanged(t){}log(t){this.options.debug&&console.warn(`[BaseClientRuntime] ${t}`)}getCore(){return this.core}getRendererManager(){return this.rendererManager}getUserId(){return this.userId}};y($,"BaseClientRuntime");var w=$;import{Core as de,PostProcessOrderType as S,PostProcessOrderCollector as le}from"@utsp/core";import{AutoplayOverlay as ce,PostProcessOverlay as te}from"@utsp/render";import{ScalingMode as he,valueToScalingMode as ue,Vector2 as pe}from"@utsp/types";import{UnifiedInputRouter as ge,InputCollector as A,MobileVibration as me}from"@utsp/input";import{SocketIOClient as fe}from"@utsp/network-client";import{AudioManager as ye}from"@utsp/audio";import{encodeCompressedInput as ae}from"@utsp/core";import{InputCollector as k}from"@utsp/input";var D=class D{constructor(t,e,i,r,n){a(this,"network");a(this,"core");a(this,"rendererManager");a(this,"performanceMonitor");a(this,"options");a(this,"audioManager",null);a(this,"postProcessCallback",null);a(this,"userId","");a(this,"lastReceivedTick",-1);a(this,"connected",!1);a(this,"totalBytesReceived",0);a(this,"lastSentViewports",new Map);a(this,"lastCollectedTouches",[]);a(this,"touchZonesCallback",null);a(this,"audioLoadingState",{totalExpected:0,loadedSounds:new Set,errors:new Map});this.network=t,this.core=e,this.rendererManager=i,this.performanceMonitor=r,this.options={serverUrl:n.serverUrl,username:n.username,token:n.token??"",debug:n.debug??!1,autoReconnect:n.autoReconnect??!0,canvasWidth:n.canvasWidth,canvasHeight:n.canvasHeight}}async connect(){this.log(`Connecting to ${this.options.serverUrl}`),await this.network.connect(),this.connected=!0,this.log("Connected to server"),this.setupNetworkHandlers()}async joinGame(t){return new Promise((e,i)=>{this.network.send("join",{username:this.options.username,token:this.options.token}),this.network.on("join_response",r=>{if(r.success){this.userId=r.userId,this.log(`Joined game as ${this.userId}`);let n=this.core.createUser(this.userId,this.options.username);n.setBytesTickRate(20),this.audioManager&&n.setAudioProcessor(this.audioManager),t?.initUser&&t.initUser(this.core,n,{username:this.options.username,token:this.options.token}),e(this.userId)}else i(new Error(r.error||"Failed to join game"))}),setTimeout(()=>i(new Error("Join timeout")),5e3)})}sendInput(t){let e=this.core.getUser(this.userId);if(!e)return;let i=this.rendererManager.getCanvas();if(i){let f=e.getDisplays(),v=f.length>0?f[0].getSize():{x:this.options.canvasWidth,y:this.options.canvasHeight},Y=v.x,j=v.y,N=this.rendererManager.getRenderer()?.getOffsets?.(),Z={offsetX:N?.offsetX??0,offsetY:N?.offsetY??0},T=k.collectMousePosition(t,i,Y,j,Z);e.setMousePosition(T.x,T.y,T.over),e.updateMacroMouse(T.x,T.y,T.isLeftDown);let J=k.collectTouchPositions(t,i,Y,j,10,Z);J.forEach(F=>{e.setTouchPosition(F.id,F.x,F.y,F.over)}),this.lastCollectedTouches=J}else this.lastCollectedTouches=[];let r=e.getInputBindingRegistry(),n=r.getAllAxes(),o=r.getAllButtons(),s=k.collectAxisSources(n,t),d=k.collectButtonSources(o,t),l=k.collectTextInputs(t);l.length>0&&e.setTextInputs(l);let c=n.sort((f,v)=>f.bindingId-v.bindingId),p=new Map;for(let f of c){let v=r.evaluateAxis(f.bindingId,s);p.set(f.bindingId,v),e.setAxis(f.name,v)}let u=o.sort((f,v)=>f.bindingId-v.bindingId),h=new Map;for(let f of u){let v=r.evaluateButton(f.bindingId,d);h.set(f.bindingId,v),e.setButton(f.name,v)}let m=e.getMouseDisplayInfo(),g=e.getIsMouseOnADisplay(),b=this.collectChangedViewports(),C=this.lastCollectedTouches.map(f=>({id:f.id,x:f.x,y:f.y,over:f.over})),I=ae(0n,p,h,m?.displayId??0,m?.localX??0,m?.localY??0,g,l,[],b,C);this.network.send("input",I)}collectChangedViewports(){let t=[],{width:e,height:i}=this.rendererManager.getAvailableSize();if(e===0||i===0)return t;let r=0,n=this.lastSentViewports.get(r);return(!n||n.pixelWidth!==e||n.pixelHeight!==i)&&(t.push({displayId:r,pixelWidth:e,pixelHeight:i}),this.lastSentViewports.set(r,{pixelWidth:e,pixelHeight:i}),this.log(`Viewport changed: display ${r} = ${e}x${i}px`)),t}disconnect(){this.connected&&(this.network.send("leave",{}),this.network.disconnect(),this.connected=!1,this.log("Disconnected from server"))}getUserId(){return this.userId}isConnected(){return this.connected}getTotalBytesReceived(){return this.totalBytesReceived}getAudioLoadingState(){let t=this.audioLoadingState.loadedSounds.size,e=this.audioLoadingState.totalExpected,i=this.audioLoadingState.errors.size,r=e>0&&t>=e,n=Array.from(this.audioLoadingState.errors.keys());return{loadedCount:t,totalExpected:e,errorCount:i,isComplete:r,errors:n}}destroy(){this.disconnect(),this.network.destroy(),this.log("NetworkSync destroyed")}setupNetworkHandlers(){this.network.on("update",t=>{try{let e=this.convertToUint8Array(t,"update");if(!e)return;this.recordBytesReceived(e.length);let i=this.core.applyUpdatePacketBuffer(this.userId,e);i?(this.postProcessCallback&&i.postProcessOrders&&i.postProcessOrders.length>0&&this.postProcessCallback(i.postProcessOrders),this.rendererManager.render(this.core,this.userId)):this.log("Failed to apply update packet")}catch(e){this.log(`Error applying update packet: ${e}`)}}),this.network.on("update-static",t=>{this.handleUpdatePacket(t,"update-static")}),this.network.on("update-dynamic",t=>{this.handleUpdatePacket(t,"update-dynamic")}),this.network.on("load",t=>{try{let e=this.convertToUint8Array(t,"load");if(!e)return;this.recordBytesReceived(e.length),this.core.applyLoadPacket(e)?this.log("Load packet applied successfully"):this.log("Failed to apply load packet")}catch(e){this.log(`Error applying load packet: ${e}`)}}),this.network.on("input-bindings",t=>{if(this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)){this.log("Input bindings configured");try{let i=JSON.parse(t);if(this.log(`Touch zones in packet: ${i.touchZones?.length??0}, callback defined: ${!!this.touchZonesCallback}`),i.touchZones&&i.touchZones.length>0&&this.touchZonesCallback){let r=i.touchZones.map(n=>({id:n.zoneId,x:n.x,y:n.y,width:n.width,height:n.height}));this.touchZonesCallback(r),this.log(`Touch zones configured: ${r.length} zones`)}}catch{}}else this.log("Failed to apply input bindings")}),this.audioManager&&this.setupSoundHandlers(),this.network.on("disconnect",()=>{this.connected=!1,this.log("Disconnected from server")})}handleUpdatePacket(t,e){try{let i=this.convertToUint8Array(t,e);if(!i)return;this.recordBytesReceived(i.length);let r=new DataView(i.buffer,i.byteOffset,i.byteLength),n=Number(r.getBigUint64(0,!1));if(e==="update-dynamic"&&n<this.lastReceivedTick)return;n>this.lastReceivedTick&&(this.lastReceivedTick=n);let o=this.core.applyUpdatePacketBuffer(this.userId,i);if(o){this.postProcessCallback&&o.postProcessOrders&&o.postProcessOrders.length>0&&this.postProcessCallback(o.postProcessOrders),this.options.debug&&this.debugRenderState();let s=performance.now();this.rendererManager.render(this.core,this.userId);let d=performance.now()-s;this.performanceMonitor.recordFrameTiming(0,d),this.options.debug&&console.warn(`[CLIENT] render() completed for ${e} (${d.toFixed(2)}ms)`)}else this.options.debug&&this.log(`Failed to apply ${e} packet (tick ${n})`)}catch(i){this.log(`Error applying ${e} update packet: ${i}`),console.error(i)}}convertToUint8Array(t,e){return t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):Array.isArray(t)?new Uint8Array(t):t.data&&Array.isArray(t.data)?new Uint8Array(t.data):typeof t=="object"&&t.type==="Buffer"?new Uint8Array(t.data):(this.log(`Unknown data type for ${e} packet: ${typeof t}`),null)}debugRenderState(){let t=this.core.getUser(this.userId);if(t){let i=t.getLayers();console.warn(`[CLIENT] User has ${i.length} layers`),i.forEach((r,n)=>{let o=r.getOrders(),s=r.getStatic();(o.length>0||s)&&console.warn(` Layer ${n}: ${o.length} orders, static=${s}, z=${r.getZOrder()}`)})}let e=this.rendererManager.isReady();console.warn(`[CLIENT] Renderer ready: ${e}`)}log(t){this.options.debug&&console.warn(`[NetworkSync] ${t}`)}recordBytesReceived(t){this.totalBytesReceived+=t;let e=this.core.getUser(this.userId);e&&e.recordBytesReceived(t)}getSoundPacketSize(t){return t.mode==="file"?t.sounds.reduce((i,r)=>i+this.getSoundDataLength(r.data)+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,0)}getSoundDataLength(t){if(t instanceof Uint8Array)return t.length;if(t instanceof ArrayBuffer||ArrayBuffer.isView(t))return t.byteLength;let e=globalThis.Buffer;return e&&t instanceof e?t.length:t&&typeof t=="object"&&Array.isArray(t.data)?t.data.length:Array.isArray(t)?t.length:0}setAudioManager(t){if(this.audioManager=t,t&&this.userId){let e=this.core.getUser(this.userId);e&&e.setAudioProcessor(t)}this.connected&&t&&this.setupSoundHandlers()}setPostProcessCallback(t){this.postProcessCallback=t}setTouchZonesCallback(t){this.touchZonesCallback=t}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),t.totalSounds!==void 0&&t.totalSounds>this.audioLoadingState.totalExpected&&(this.audioLoadingState.totalExpected=t.totalSounds,this.log(`Audio loading: expecting ${t.totalSounds} total sounds`)),t.mode==="file"?await this.handleSoundLoad(t):t.mode==="external"&&await this.handleSoundExternalLoad(t)}),this.log("Sound handlers registered"))}async handleSoundLoad(t){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}for(let i of t.sounds)try{let r=this.normalizeSoundData(i.data);await e.loadFromData(i.soundId,i.name,r),this.log(`Loaded sound: ${i.name} (${r.length} bytes)`),this.audioLoadingState.loadedSounds.add(i.name),this.audioLoadingState.errors.delete(i.name),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.audioLoadingState.errors.set(i.name,String(r)),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}normalizeSoundData(t){if(t instanceof Uint8Array)return t;if(t instanceof ArrayBuffer)return new Uint8Array(t);if(ArrayBuffer.isView(t))return new Uint8Array(t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength));let e=globalThis.Buffer;if(e&&t instanceof e)return new Uint8Array(t);if(t&&typeof t=="object"&&Array.isArray(t.data))return new Uint8Array(t.data);if(Array.isArray(t))return new Uint8Array(t);throw new Error("Invalid sound data payload")}async handleSoundExternalLoad(t){if(!this.audioManager){this.log("Cannot load external sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load external sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromUrl(i.soundId,i.name,i.url),this.log(`Loaded external sound: ${i.name} from ${i.url}`),this.audioLoadingState.loadedSounds.add(i.name),this.audioLoadingState.errors.delete(i.name),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,r),this.audioLoadingState.errors.set(i.name,String(r)),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}sendAudioAck(t){this.network.send("audio-ack",t),this.log(`Sent audio-ack: ${t.type}`)}sendBridge(t,e){this.network.sendBridge(t,e),this.log(`Bridge sent on channel '${t}'`)}onBridge(t,e){this.network.onBridge(t,e),this.log(`Bridge handler registered for channel '${t}'`)}offBridge(t,e){this.network.offBridge(t,e),this.log(`Bridge handler removed for channel '${t}'`)}removeAllBridgeHandlers(t){this.network.removeAllBridgeHandlers(t),this.log(t?`All bridge handlers removed for '${t}'`:"All bridge handlers removed")}};y(D,"NetworkSync");var B=D;var G=class G extends w{constructor(e){super({application:e.application,container:e.container,debug:e.debug,width:e.width,height:e.height,renderer:e.renderer,useImageDataRendering:e.useImageDataRendering,renderMode:e.renderMode});a(this,"input",null);a(this,"networkSync",null);a(this,"mode");a(this,"localOptions",null);a(this,"autoplayOverlay",null);a(this,"autoplay",!0);a(this,"audioManager",null);a(this,"mobileVibration",null);a(this,"postProcessOverlay",null);a(this,"postProcessOrderCollector",new le);a(this,"localBridgeHandlers",new Map);a(this,"localBridgeWildcardHandlers",new Set);if(this.mode=e.mode,e.mode==="local"?(this.localOptions={mode:"local",application:e.application,container:e.container,debug:e.debug??!1,width:e.width??80,height:e.height??25,userId:e.userId??"local",username:e.username??"User",renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,touchZones:e.touchZones,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.userId=e.userId??"local",this.autoplay=e.autoplay??!0):(this.localOptions={mode:"connected",application:e.application,container:e.container,serverUrl:e.serverUrl,username:e.username??"Player",debug:e.debug??!1,width:e.width??80,height:e.height??25,autoReconnect:e.autoReconnect??!0,token:e.token,renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,touchZones:e.touchZones,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.autoplay=e.autoplay??!0),this.log(`Initializing ClientRuntime (${this.mode} mode)`),this.mode==="connected"&&(this.core=new de({mode:"client",maxUsers:100}),this.core.onPaletteChanged(r=>{this.onCorePaletteChanged(r)}),this.core.onBitmapFontChanged(r=>{this.onCoreBitmapFontChanged(r)}),this.core.onImageFontChanged(r=>{this.onCoreImageFontChanged(r)})),e.inputEnabled??!0?this.input=new ge({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:e.touchZones?.targetElement??void 0,debug:e.debug,keyboardConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mouseConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mobileConfig:{preventDefault:e.mobileInputConfig?.preventDefault??!1,passive:e.mobileInputConfig?.passive??!0,maxTouches:e.mobileInputConfig?.maxTouches??10},enableTouchZones:e.touchZones?.enable??!1,touchZoneConfig:{targetElement:e.touchZones?.targetElement,gridWidth:e.touchZones?.gridWidth??e.width??0,gridHeight:e.touchZones?.gridHeight??e.height??0,rendererOffsets:e.touchZones?.rendererOffsets,zones:e.touchZones?.zones}}):this.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new fe({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:e.debug});this.networkSync=new B(n,this.core,this.rendererManager,this.performanceMonitor,{serverUrl:r.serverUrl,username:r.username,token:r.token,debug:e.debug,autoReconnect:r.autoReconnect,canvasWidth:e.width??80,canvasHeight:e.height??25})}this.log("ClientRuntime initialized")}getMode(){return this.mode}setTickRate(e){if(this.mode==="connected"){this.log("setTickRate() has no effect in connected mode");return}super.setTickRate(e)}async onBeforeStart(){this.audioManager=new ye({debug:this.options.debug}),this.mobileVibration=new me({debug:this.options.debug}),this.autoplay?(this.log("Autoplay enabled, initializing AudioManager..."),this.audioManager.initialize()):(this.log("Autoplay disabled, showing overlay..."),await new Promise(e=>{this.autoplayOverlay=new ce(this.options.container,{...this.localOptions?.autoplayOptions,onStart:()=>{this.log("User clicked start button"),this.audioManager&&(this.audioManager.initialize(),this.audioManager.playStartSound()),this.mobileVibration&&this.mobileVibration.vibrate(50),e()}})}),this.log("Autoplay overlay dismissed, continuing startup..."))}async onAfterInit(){this.postProcessOverlay=new te(this.options.container),this.log("PostProcessOverlay created");let e=this.rendererManager.getCanvas();if(e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync){if(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),this.input){let r=this.input,n=this.rendererManager;this.networkSync.setTouchZonesCallback(o=>{this.log(`[onAfterInit] Touch zones callback called with ${o.length} zones`);let s=n.getCanvas();if(this.log(`[onAfterInit] Canvas available: ${!!s}`),s){let d=o.reduce((p,u)=>Math.max(p,u.x+u.width),0),l=o.reduce((p,u)=>Math.max(p,u.y+u.height),0);this.log(`[onAfterInit] Grid from zones: ${d}x${l}`);let c=r.enableTouchZonesDevice(s,d,l,o);this.log(`[onAfterInit] enableTouchZonesDevice returned: ${c}`),c&&this.log(`Touch zones configured from server: ${o.length} zones`)}})}if(await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application),this.input){let n=this.core.getUser(this.userId)?.getDisplays()??[];if(n.length>0){let o=n[0].getSize();this.input.updateTouchZoneLayout(o.x,o.y)}}let i=this.core.getUser(this.userId);i&&this.configureUserProcessors(i)}}async start(){if(this.running){this.log("Already running");return}this.log("Starting ClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.postProcessOverlay=new te(this.options.container),this.log("PostProcessOverlay created"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.options.application&&(this.log("Calling application.init()"),await this.options.application.init(this.core,this));let e=this.rendererManager.getCanvas();if(e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync){if(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(n=>{this.applyPostProcessOrders(n)}),this.input){let n=this.input,o=this.rendererManager;this.networkSync.setTouchZonesCallback(s=>{this.log(`[start] Touch zones callback called with ${s.length} zones`);let d=o.getCanvas();if(this.log(`[start] Canvas available: ${!!d}`),d){let l=s.reduce((u,h)=>Math.max(u,h.x+h.width),0),c=s.reduce((u,h)=>Math.max(u,h.y+h.height),0);this.log(`[start] Grid from zones: ${l}x${c}`);let p=n.enableTouchZonesDevice(d,l,c,s);this.log(`[start] enableTouchZonesDevice returned: ${p}`),p&&this.log(`Touch zones configured from server: ${s.length} zones`)}})}await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application);let r=this.core.getUser(this.userId);r&&this.configureUserProcessors(r)}else await this.createLocalUser();let i=this.core.getUser(this.userId);if(i){let r=i.getDisplays();if(r.length>0){let o=r[0].getSize(),s=o.x,d=o.y,l=this.rendererManager.getRenderer(),c,p;if(this.rendererType==="webgl"){let h=l.getGridSize();c=h.cols,p=h.rows}else{let u=l;c=u.getCols(),p=u.getRows()}(c!==s||p!==d)&&(this.log(`Adjusting renderer from ${c}\xD7${p} to match display ${s}\xD7${d}`),l.resize(s,d))}}this.input&&this.input.start(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onStop(){this.audioManager&&this.audioManager.stopAll(),this.input&&this.input.stop(),this.mode==="connected"&&this.networkSync&&this.networkSync.disconnect()}getStats(){let e=super.getStats(),r=this.core.getUser(this.userId)?.getTotalBytesReceived();return{...e,mode:this.mode,latency:void 0,totalBytesReceived:r}}getAudioLoadingState(){if(this.mode==="connected"&&this.networkSync)return this.networkSync.getAudioLoadingState();if(this.audioManager){let e=this.audioManager.getSoundBank();if(e){let i=e.getStats();return{loadedCount:i.totalSounds,totalExpected:i.totalSounds,errorCount:0,isComplete:!0,errors:[]}}}return{loadedCount:0,totalExpected:0,errorCount:0,isComplete:!0,errors:[]}}async onDestroy(){this.autoplayOverlay&&(this.autoplayOverlay.destroy(),this.autoplayOverlay=null),this.audioManager&&(this.audioManager.destroy(),this.audioManager=null),this.postProcessOverlay&&(this.postProcessOverlay.destroy(),this.postProcessOverlay=null),this.input&&this.input.destroy(),this.networkSync&&this.networkSync.destroy()}configureUserProcessors(e){if(this.audioManager&&e.setAudioProcessor(this.audioManager),this.mobileVibration&&e.setMobileVibrationProcessor(this.mobileVibration),this.input){let i=this.input.getGamepad();i&&e.setGamepadVibrationProcessor(i)}}async createLocalUser(){let e=this.localOptions;this.userId=e?.userId??"local",this.log(`Creating local user: ${this.userId}`);let i=this.core.createUser(this.userId,e?.username??"User");if(i.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.configureUserProcessors(i),this.options.application?.initUser&&(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,i,{username:e?.username??"User"})),i.hasAudioConfigCommands()&&(this.log("Applying audio config commands from initUser()"),i.applyAudioConfigCommands(i.flushAudioConfigCommands())),i.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let d=i.flushMacroOrders();i.applyMacroOrders(d)}if(i.hasPostProcessCommands()){this.log("Applying post-process commands from initUser()");let d=i.flushPostProcessCommands(),l=this.postProcessOrderCollector.convertCommands(d);this.applyPostProcessOrders(l)}i.needsSendSounds()&&(await this.loadSoundsFromRegistry(),i.clearSendSounds());let r=this.core.generateAllLoadPackets();r.forEach(d=>{i.recordBytesReceived(d.length)});let n=this.core.generateMacroLoadPackets(this.userId);n.forEach(d=>{i.recordBytesReceived(d.length)});let o=i.getInputBindingsLoadPacket();if(o&&(i.recordBytesReceived(o.length),this.input)){let l=i.getInputBindingRegistry().getAllTouchZones();if(l.length>0){let c=this.rendererManager.getCanvas();if(c){let p=i.getDisplays(),u=p.length>0?p[0].getSize():{x:this.options.width??80,y:this.options.height??25},h=l.map(g=>({id:g.zoneId,x:g.x,y:g.y,width:g.width,height:g.height}));this.input.enableTouchZonesDevice(c,u.x,u.y,h)?this.log(`Touch zones synchronized from registry: ${l.length} zones`):this.log("Failed to enable touch zones - mobile input may not be initialized")}}}this.log(`Simulated ${r.length} load packets, ${n.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach(({static:d,dynamic:l},c)=>{let p=this.core.getUser(c);p&&(d&&p.recordBytesReceived(d.length),l&&p.recordBytesReceived(l.length))}),this.render(),this.log("Initial render complete")}async loadSoundsFromRegistry(){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=this.core.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry into AudioManager...`);let n=this.core.getUser(this.userId),o=0;for(let s of r)try{if(s.loadType==="file"&&s.data){await e.loadFromData(s.soundId,s.name,s.data);let d=s.data.length+s.name.length+10;o+=d,this.log(`\u{1F50A} Loaded sound: ${s.name} (${s.data.length} bytes)`)}else if(s.loadType==="external"&&s.url){await e.loadFromUrl(s.soundId,s.name,s.url);let d=s.url.length+s.name.length+10;o+=d,this.log(`\u{1F50A} Loaded external sound: ${s.name} from ${s.url}`)}}catch(d){console.error(`[ClientRuntime] Failed to load sound "${s.name}":`,d)}n&&o>0&&(n.recordBytesReceived(o),this.log(`Simulated ${o} bytes for ${r.length} sound(s)`)),this.log(`\u2713 ${e.size} sounds loaded into AudioManager`)}mainLoop(e){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(e),this.render()),this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}let i=e-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&i<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}this.lastRenderTimestamp=e;let r=(e-this.lastTimestamp)/1e3,n=Math.min(r,.1);this.lastTimestamp=e,this.performanceMonitor.updateFPS(e);let o=this.core.getUser(this.userId);if(!o){this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}if(this.mode==="local"){if(this.tickRate===0){let c=performance.now();this.render();let p=performance.now()-c;this.performanceMonitor.recordFrameTiming(0,p),this.rafId=requestAnimationFrame(u=>this.mainLoop(u));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=e,this.accumulatedTime=0,this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.accumulatedTime+=n;let s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let d=0,l=0;for(;this.accumulatedTime>=s&&d<5;){let c=performance.now();if(this.collectAndApplyInput(o),this.options.application&&(this.options.application.update?.(this.core,s),this.options.application.updateUser?.(this.core,o,s)),o.clearTextInputs(),o.hasAudioConfigCommands()&&o.applyAudioConfigCommands(o.flushAudioConfigCommands()),o.hasSoundCommands()&&o.applyAudioCommands(o.flushSoundCommands()),o.hasMobileVibrationCommands()&&o.applyMobileVibrationCommands(o.flushMobileVibrationCommands()),o.hasGamepadVibrationCommands()&&o.applyGamepadVibrationCommands(o.flushGamepadVibrationCommands()),o.hasPostProcessCommands()){let h=o.flushPostProcessCommands(),m=this.postProcessOrderCollector.convertCommands(h);this.applyPostProcessOrders(m)}if(o.hasPendingMacroOrders()){let h=o.flushMacroOrders();o.applyMacroOrders(h)}let p=o.updateMacros();if(o.processMacroEvents(p),o.hasBridgeMessages()){let h=o.getBridgeMessages();this.dispatchLocalBridgeMessages(h)}this.core.endTick().forEach(({static:h,dynamic:m})=>{let g=(h?.length??0)+(m?.length??0);o.recordBytesReceived(g)}),l+=performance.now()-c,this.accumulatedTime-=s,d++}if(d>0){let c=performance.now();this.render();let p=performance.now()-c;this.performanceMonitor.recordFrameTiming(l,p)}}else{let s=o.updateMacros();o.processMacroEvents(s),this.collectAndSendInput()}this.rafId=requestAnimationFrame(s=>this.mainLoop(s))}collectAndApplyInput(e){if(!this.input)return;let i=this.rendererManager.getCanvas();if(i){let d=e.getDisplays(),l=d.length>0?d[0].getSize():{x:this.options.width,y:this.options.height},c=l.x,p=l.y,h=this.rendererManager.getRenderer()?.getOffsets?.(),m={offsetX:h?.offsetX??0,offsetY:h?.offsetY??0};this.input.updateTouchZoneLayout(c,p,m);let g=A.collectMousePosition(this.input,i,c,p,m);e.setMousePosition(g.x,g.y,g.over),e.updateMacroMouse(g.x,g.y,g.isLeftDown),A.collectTouchPositions(this.input,i,c,p,10,m).forEach(I=>{e.setTouchPosition(I.id,I.x,I.y,I.over)});let C=this.rendererManager.getAvailableSize();e.setDisplayViewport(0,C.width,C.height)}let r=A.collectTextInputs(this.input);r.length>0&&e.setTextInputs(r);let n=e.getInputBindingRegistry(),o=n.getAllAxes(),s=n.getAllButtons();if(o.length>0||s.length>0){let d=A.collectAxisSources(o,this.input),l=A.collectButtonSourcesWithTransitions(s,this.input);o.forEach(c=>{let p=n.evaluateAxis(c.bindingId,d);e.setAxis(c.name,p)}),s.forEach(c=>{let p=new Map,u=new Map,h=new Map;for(let[C,I]of l)p.set(C,I.pressed),u.set(C,I.justPressed),h.set(C,I.justReleased);let m=n.evaluateButton(c.bindingId,p),g=n.evaluateButton(c.bindingId,u),b=n.evaluateButton(c.bindingId,h);e.setButton(c.name,m),e.setButton(`${c.name}_justPressed`,g),e.setButton(`${c.name}_justReleased`,b)})}this.input.poll?.()}collectAndSendInput(){!this.networkSync||!this.input||this.networkSync.sendInput(this.input)}render(){let e=this.core.getUser(this.userId);if(e){let i=e.getDisplays();if(i.length>0){let r=i[0],n=r.getSize(),o=this.rendererManager.getRenderer();if(o){let s,d,l,c,p=null;if(this.rendererType==="webgl"){let h=o,m=h.getGridSize();s=m.cols,d=m.rows,l=h.getCellWidth(),c=h.getCellHeight(),p=h.getScalingMode()}else{let h=o;s=h.getCols(),d=h.getRows(),l=h.getCellWidth(),c=h.getCellHeight(),p=h.getScalingMode()}if(p===he.Responsive){let h=this.rendererManager.getAvailableSize(),m=256*256;"getMaxCells"in o&&(m=Math.min(m,o.getMaxCells()));let g=Math.floor(h.width/l),b=Math.floor(h.height/c);if(g=Math.min(256,Math.max(1,g)),b=Math.min(256,Math.max(1,b)),g*b>m){let C=Math.sqrt(m/(g*b));g=Math.max(1,Math.floor(g*C)),b=Math.max(1,Math.floor(b*C)),this.log(`\u26A0\uFE0F Device limit: max ${m} cells, clamping to ${g}\xD7${b}`)}(n.x!==g||n.y!==b)&&(this.log(`\u{1F4D0} Resizing display from ${n.x}\xD7${n.y} to ${g}\xD7${b} (available: ${h.width}\xD7${h.height}px, cell: ${l}\xD7${c}px)`),r.setSize(new pe(g,b)))}let u=r.getSize();(s!==u.x||d!==u.y)&&(this.log(`\u{1F4D0} Resizing renderer from ${s}\xD7${d} to ${u.x}\xD7${u.y}`),o.resize(u.x,u.y))}}}super.render()}onFontChanged(e){this.postProcessOverlay&&"syncWithRenderer"in this.postProcessOverlay&&"getCellWidth"in e&&"getCellHeight"in e&&"getCurrentScale"in e&&"getGridSize"in e&&(this.postProcessOverlay.syncWithRenderer(e),this.log("\u2713 PostProcessOverlay synced with renderer dimensions"))}getAudioManager(){return this.audioManager}getAudioContext(){return this.audioManager?.getContext()??null}getPostProcessOverlay(){return this.postProcessOverlay}applyAmbientEffectConfig(e){let i=this.rendererManager?.getRenderer();i&&"setAmbientEffect"in i&&(e.enabled?i.setAmbientEffect({blur:e.blur,scale:e.scale}):i.setAmbientEffect(!1),this.log(`Ambient effect ${e.enabled?"enabled":"disabled"}`))}applyPostProcessOrders(e){if(!this.postProcessOverlay){this.log("PostProcessOverlay not initialized, skipping post-process orders");return}for(let i of e){let r=i.displayId;switch(i.type){case S.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.postProcessOverlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffectConfig(n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case S.SetScanlines:{this.postProcessOverlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case S.SetAmbientEffect:{this.applyAmbientEffectConfig({enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case S.SetScalingMode:{this.applyScalingMode(r,i.mode);break}case S.SetGrid:{this.applyGridConfig(r,i);break}case S.SwitchPalette:{this.applySwitchPalette(r,i);break}case S.SetCellSize:{this.applyCellSize(r,i.cellWidth,i.cellHeight);break}}}}applyScalingMode(e,i){let r=this.rendererManager.getRenderer();if(r&&"setScalingMode"in r){let n=ue(i);r.setScalingMode(n),this.log(`[Display ${e}] Scaling mode set to ${n}`)}}applyGridConfig(e,i){let r=this.rendererManager.getRenderer();if(r&&"setGrid"in r){let n=i.colorA/255;r.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${n})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}applySwitchPalette(e,i){let r=this.core.getPaletteFromSlot(i.slotId);if(!r){console.warn(`[ClientRuntime] Palette slot ${i.slotId} not found`);return}let n=[];for(let s=0;s<256;s++){let d=r.get(s);d?n.push({r:d.r,g:d.g,b:d.b,a:d.a}):n.push({r:0,g:0,b:0,a:0})}let o=this.rendererManager.getRenderer();o&&"setPalette"in o&&(o.setPalette(n),this.log(`[Display ${e}] Switched to palette slot ${i.slotId}`))}applyCellSize(e,i,r){let n=this.rendererManager.getRenderer();n&&"setCellSize"in n&&(n.setCellSize(i,r),this.log(`[Display ${e}] Cell size set to ${i}\xD7${r}px`))}patternFromType(e){switch(e){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}dispatchLocalBridgeMessages(e){for(let i of e){let{channel:r,data:n}=i,o=this.localBridgeHandlers.get(r);if(o)for(let s of o)try{s(n)}catch(d){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,d)}for(let s of this.localBridgeWildcardHandlers)try{s(r,n)}catch(d){console.error("[ClientRuntime] Bridge wildcard handler error:",d)}this.log(`Bridge message dispatched on channel '${r}'`)}}sendBridge(e,i){if(this.mode==="connected"&&this.networkSync)this.networkSync.sendBridge(e,i);else if(this.mode==="local"){let r=this.core.getUser(this.userId);r&&this.options.application?.onBridgeMessage&&(this.options.application.onBridgeMessage(this.core,r,e,i),this.log(`Bridge message sent to application on channel '${e}'`))}}onBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.onBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.add(i):(this.localBridgeHandlers.has(e)||this.localBridgeHandlers.set(e,new Set),this.localBridgeHandlers.get(e).add(i)),this.log(`Bridge handler registered for channel '${e}'`))}offBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.offBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.delete(i):this.localBridgeHandlers.get(e)?.delete(i))}removeAllBridgeHandlers(e){this.mode==="connected"&&this.networkSync?this.networkSync.removeAllBridgeHandlers(e):this.mode==="local"&&(e===void 0?(this.localBridgeHandlers.clear(),this.localBridgeWildcardHandlers.clear()):e==="*"?this.localBridgeWildcardHandlers.clear():this.localBridgeHandlers.delete(e))}log(e){this.options.debug&&console.warn(`[ClientRuntime/${this.mode}] ${e}`)}};y(G,"ClientRuntime");var V=G;import{UnifiedInputRouter as be,InputCollector as P,MobileVibration as ve}from"@utsp/input";var q=class q{constructor(t={}){a(this,"input");a(this,"mobileVibration");this.input=new be({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:t.debug,keyboardConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mouseConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mobileConfig:{preventDefault:t.mobileInputConfig?.preventDefault??!1,passive:t.mobileInputConfig?.passive??!0,maxTouches:t.mobileInputConfig?.maxTouches??10}}),this.mobileVibration=new ve({debug:t.debug})}setMobileTarget(t){this.input.setMobileTarget(t)}start(){this.input.start()}stop(){this.input.stop()}destroy(){this.input.destroy()}getRouter(){return this.input}getGamepad(){return this.input.getGamepad()}getMobileVibration(){return this.mobileVibration}collectAndApply(t,e,i,r,n){if(e){let c=P.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(c.x,c.y,c.over),t.updateMacroMouse(c.x,c.y,c.isLeftDown),P.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let o=P.collectTextInputs(this.input);o.length>0&&t.setTextInputs(o);let s=t.getInputBindingRegistry(),d=s.getAllAxes(),l=s.getAllButtons();if(d.length>0||l.length>0){let c=P.collectAxisSources(d,this.input),p=P.collectButtonSourcesWithTransitions(l,this.input);d.forEach(u=>{let h=s.evaluateAxis(u.bindingId,c);t.setAxis(u.name,h)}),l.forEach(u=>{let h=new Map,m=new Map,g=new Map;for(let[f,v]of p)h.set(f,v.pressed),m.set(f,v.justPressed),g.set(f,v.justReleased);let b=s.evaluateButton(u.bindingId,h),C=s.evaluateButton(u.bindingId,m),I=s.evaluateButton(u.bindingId,g);t.setButton(u.name,b),t.setButton(`${u.name}_justPressed`,C),t.setButton(`${u.name}_justReleased`,I)})}this.input.poll?.()}processVibrationCommands(t){t.hasMobileVibrationCommands()&&t.applyMobileVibrationCommands(t.flushMobileVibrationCommands()),t.hasGamepadVibrationCommands()&&t.applyGamepadVibrationCommands(t.flushGamepadVibrationCommands())}};y(q,"InputFeature");var z=q;import{AudioManager as Ce}from"@utsp/audio";var H=class H{constructor(t={}){a(this,"audioManager");a(this,"debug");this.debug=t.debug??!1,this.audioManager=new Ce({debug:t.debug})}initialize(){this.audioManager.initialize()}playStartSound(){this.audioManager.playStartSound()}stopAll(){this.audioManager.stopAll()}destroy(){this.audioManager.destroy()}getManager(){return this.audioManager}getContext(){return this.audioManager.getContext()}injectIntoUser(t){t.setAudioProcessor(this.audioManager)}processAudioCommands(t){t.hasAudioConfigCommands()&&t.applyAudioConfigCommands(t.flushAudioConfigCommands()),t.hasSoundCommands()&&t.applyAudioCommands(t.flushSoundCommands())}async loadSoundsFromRegistry(t){let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=t.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry...`);for(let n of r)try{n.loadType==="file"&&n.data?(await e.loadFromData(n.soundId,n.name,n.data),this.log(`Loaded sound: ${n.name} (${n.data.length} bytes)`)):n.loadType==="external"&&n.url&&(await e.loadFromUrl(n.soundId,n.name,n.url),this.log(`Loaded external sound: ${n.name} from ${n.url}`))}catch(o){console.error(`[AudioFeature] Failed to load sound "${n.name}":`,o)}this.log(`${e.size} sounds loaded`)}log(t){this.debug&&console.warn(`[AudioFeature] ${t}`)}};y(H,"AudioFeature");var W=H;import{PostProcessOrderCollector as Ie,PostProcessOrderType as U}from"@utsp/core";import{PostProcessOverlay as Re}from"@utsp/render";import{valueToScalingMode as Se}from"@utsp/types";var K=class K{constructor(t,e={}){a(this,"overlay");a(this,"orderCollector");a(this,"debug");this.debug=e.debug??!1,this.overlay=new Re(t),this.orderCollector=new Ie}destroy(){this.overlay.destroy()}getOverlay(){return this.overlay}syncWithRenderer(t){this.overlay.syncWithRenderer(t),this.log("Overlay synced with renderer dimensions")}processCommands(t,e){if(!t.hasPostProcessCommands())return;let i=t.flushPostProcessCommands(),r=this.orderCollector.convertCommands(i);this.applyOrders(r,e)}applyOrders(t,e){for(let i of t){let r=i.displayId;switch(i.type){case U.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.overlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffect(e,n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case U.SetScanlines:{this.overlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case U.SetAmbientEffect:{this.applyAmbientEffect(e,{enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case U.SetScalingMode:{this.applyScalingMode(e,r,i.mode);break}case U.SetGrid:{this.applyGridConfig(e,r,i);break}}}}applyAmbientEffect(t,e){t&&"setAmbientEffect"in t&&(e.enabled?t.setAmbientEffect({blur:e.blur,scale:e.scale}):t.setAmbientEffect(!1))}applyScalingMode(t,e,i){if(t&&"setScalingMode"in t){let r=Se(i);t.setScalingMode(r),this.log(`[Display ${e}] Scaling mode set to ${r}`)}}applyGridConfig(t,e,i){if(t&&"setGrid"in t){let r=i.colorA/255;t.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${r})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}patternFromType(t){switch(t){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}log(t){this.debug&&console.warn(`[PostProcessFeature] ${t}`)}};y(K,"PostProcessFeature");var Q=K;import{ScalingMode as St}from"@utsp/render";import{AudioManager as Mt}from"@utsp/audio";export{W as AudioFeature,Mt as AudioManager,w as BaseClientRuntime,V as ClientRuntime,z as InputFeature,Q as PostProcessFeature,_ as RendererType,St as ScalingMode};
|
|
1
|
+
var ie=Object.defineProperty;var ne=(I,t,e)=>t in I?ie(I,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):I[t]=e;var R=(I,t)=>ie(I,"name",{value:t,configurable:!0});var d=(I,t,e)=>(ne(I,typeof t!="symbol"?t+"":t,e),e);import{Core as oe}from"@utsp/core";import{TerminalGL as ae,Terminal2D as de}from"@utsp/render";var re=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(re||{});var L=class L{constructor(t=60){d(this,"frameCount",0);d(this,"fps",0);d(this,"fpsUpdateTime",0);d(this,"lastCoreTime",0);d(this,"lastRenderTime",0);d(this,"lastTotalTime",0);d(this,"coreTimeSamples",[]);d(this,"renderTimeSamples",[]);d(this,"totalTimeSamples",[]);d(this,"maxSamples");if(t<=0)throw new Error("maxSamples must be positive");this.maxSamples=t}startTracking(t){this.fpsUpdateTime=t,this.frameCount=0,this.fps=0}updateFPS(t){this.frameCount++,t-this.fpsUpdateTime>=1e3&&(this.fps=Math.round(this.frameCount*1e3/(t-this.fpsUpdateTime)),this.frameCount=0,this.fpsUpdateTime=t)}recordFrameTiming(t,e){let i=t+e;this.lastCoreTime=t,this.lastRenderTime=e,this.lastTotalTime=i,this.coreTimeSamples.push(t),this.renderTimeSamples.push(e),this.totalTimeSamples.push(i),this.coreTimeSamples.length>this.maxSamples&&(this.coreTimeSamples.shift(),this.renderTimeSamples.shift(),this.totalTimeSamples.shift())}getFPS(){return this.fps}getLastFrameTiming(){if(!(this.lastCoreTime===0&&this.lastRenderTime===0))return{coreTime:this.lastCoreTime,renderTime:this.lastRenderTime,totalTime:this.lastTotalTime}}getAverageFrameTiming(){if(this.coreTimeSamples.length!==0)return{coreTime:this.average(this.coreTimeSamples),renderTime:this.average(this.renderTimeSamples),totalTime:this.average(this.totalTimeSamples)}}getStats(){return{fps:this.fps,lastFrame:this.getLastFrameTiming(),avgFrame:this.getAverageFrameTiming(),sampleCount:this.coreTimeSamples.length,maxSamples:this.maxSamples}}reset(){this.frameCount=0,this.fps=0,this.fpsUpdateTime=0,this.lastCoreTime=0,this.lastRenderTime=0,this.lastTotalTime=0,this.coreTimeSamples=[],this.renderTimeSamples=[],this.totalTimeSamples=[]}average(t){return t.length===0?0:t.reduce((e,i)=>e+i,0)/t.length}};R(L,"PerformanceMonitor");var B=L;var se="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",T={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function $(){let I=atob(se),t=new Uint8Array(I.length);for(let e=0;e<I.length;e++)t[e]=I.charCodeAt(e);return t}R($,"decodeDefaultAtlas");var V=class V{constructor(t,e={}){d(this,"renderer");d(this,"options");d(this,"layerFilter",null);this.renderer=t,this.options={debug:e.debug??!1,useImageDataRendering:e.useImageDataRendering??!1}}setLayerFilter(t){this.layerFilter=t}getLayerFilter(){return this.layerFilter}async initialize(t){if("setImageDataRendering"in this.renderer&&this.options.useImageDataRendering&&(this.renderer.setImageDataRendering(!0),this.log("ImageData rendering enabled")),"setImageFont"in this.renderer&&typeof this.renderer.setImageFont=="function"){this.log("Loading default 8x8 font atlas...");let e=$();await this.renderer.setImageFont(e,T.glyphWidth,T.glyphHeight,T.cellWidth,T.cellHeight,T.atlasBlocks),this.log("Default 8x8 font atlas loaded")}await this.waitForReady(),this.log("Initialized and ready")}async waitForReady(t=100,e=50){this.log("Waiting for renderer to be ready");let i=0;return new Promise((r,n)=>{let s=R(()=>{if(i++,this.renderer.isReady())this.log(`Renderer ready after ${i*e}ms`),r();else if(i>=t){let a=`Renderer failed to be ready after ${t*e}ms`;this.log(a),n(new Error(a))}else setTimeout(s,e)},"check");s()})}async loadDefaultFont(t){console.warn("[RendererManager] loadDefaultFont() is deprecated. Use ImageFont instead.")}renderDisplay(t){return this.renderer.isReady()?t?(this.renderer.renderDisplayData(t),!0):(console.warn("[RENDERER MANAGER] No display provided"),!1):(console.warn("[RENDERER MANAGER] Renderer not ready"),!1)}render(t,e){let i=this.layerFilter?t.getRenderState(e,this.layerFilter):t.getRenderState(e);return!i||i.displays.length===0?(console.warn("[RENDERER MANAGER] No render state or no displays"),!1):this.renderDisplay(i.displays[0])}getRenderer(){return this.renderer}getCanvas(){return this.renderer.getCanvas()}getAvailableSize(){if(this.renderer.getAvailableSize)return this.renderer.getAvailableSize();let t=this.getCanvas();return{width:t?.clientWidth||t?.width||0,height:t?.clientHeight||t?.height||0}}isReady(){return this.renderer.isReady()}destroy(){this.log("Destroying renderer"),this.renderer.destroy()}log(t){this.options.debug&&console.warn(`[RendererManager] ${t}`)}};R(V,"RendererManager");var k=V;var G=class G{constructor(t){d(this,"core");d(this,"rendererManagers",new Map);d(this,"rendererInitPromises",new Map);d(this,"rendererManager");d(this,"rendererType");d(this,"primaryDisplayId",0);d(this,"displayConfigs");d(this,"options");d(this,"running",!1);d(this,"startTime",0);d(this,"lastTimestamp",0);d(this,"userId","");d(this,"visibilityChangeHandler");d(this,"tickRate",30);d(this,"accumulatedTime",0);d(this,"FRAME_TIME_MIN",1e3/60);d(this,"lastRenderTimestamp",0);d(this,"rafId",0);d(this,"firstTickDone",!1);d(this,"performanceMonitor");d(this,"renderMode","continuous");d(this,"renderRequested",!1);if(this.options={application:t.application,container:t.container,displayContainers:t.displayContainers,debug:t.debug??!1,width:t.width??80,height:t.height??25,renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,renderMode:t.renderMode??"continuous"},this.log("Initializing BaseClientRuntime"),this.core=new oe({mode:"client",maxUsers:1}),this.core.onPaletteChanged(e=>{this.onCorePaletteChanged(e)}),this.core.onBitmapFontChanged(e=>{this.onCoreBitmapFontChanged(e)}),this.core.onImageFontChanged(e=>{this.onCoreImageFontChanged(e)}),this.visibilityChangeHandler=()=>{!document.hidden&&this.running&&(this.lastTimestamp=performance.now(),this.accumulatedTime=0,this.log("Tab visible: Reset timing"))},document.addEventListener("visibilitychange",this.visibilityChangeHandler),this.rendererType=this.options.renderer,this.options.displayContainers&&this.options.displayContainers.length>0){this.displayConfigs=new Map([[0,{container:this.options.container,renderer:this.options.renderer}]]);for(let e of this.options.displayContainers){let i=e.renderer;this.displayConfigs.set(e.id,{container:e.container,renderer:i})}this.primaryDisplayId=0}else this.displayConfigs=new Map([[0,{container:this.options.container,renderer:this.options.renderer}]]),this.primaryDisplayId=0;this.createRendererManagers(),this.performanceMonitor=new B(60),this.tickRate=20,this.renderMode=t.renderMode??"continuous",this.log(`Configured: tickRate=${this.tickRate}, renderMode=${this.renderMode}`),this.log("BaseClientRuntime initialized")}createRenderer(t,e){if(e==="terminal2d"){this.log("Creating Terminal 2D renderer");let s=new de(t,{fixedCols:this.options.width,fixedRows:this.options.height,cellAspectRatio:1});return this.options.useImageDataRendering&&(this.log("Enabling ImageData rendering (pixel-perfect, optimized)"),s.setImageDataRendering(!0)),this.log("Canvas 2D renderer created successfully"),s}this.log("Creating TerminalGL renderer");let r=document.createElement("canvas").getContext("webgl");if(!r)throw new Error("WebGL not supported. TerminalGL requires WebGL 1.0.");if(!r.getExtension("OES_element_index_uint"))throw new Error("OES_element_index_uint extension not supported. TerminalGL requires this extension for large terminals (256x256).");if(!(t instanceof HTMLDivElement))throw new Error(`TerminalGL requires container to be an HTMLDivElement. Received: ${t.tagName}`);let n=new ae(t,{cols:this.options.width,rows:this.options.height,charWidth:8,charHeight:8});return this.log("TerminalGL created successfully"),n}createRendererManagers(){let t=this.displayConfigs.size>=3;this.rendererManagers.clear();for(let[i,r]of this.displayConfigs.entries()){let n=t?"terminal2d":r.renderer??this.options.renderer,s=this.createRenderer(r.container,n??"webgl"),o=new k(s,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering});this.rendererManagers.set(i,o)}let e=this.rendererManagers.get(this.primaryDisplayId);if(!e)throw new Error(`Primary display ${this.primaryDisplayId} has no renderer. Check displayContainers configuration.`);this.rendererManager=e}ensureRendererManager(t){let e=this.rendererManagers.get(t);if(e)return e;let i=document.createElement("div");i.dataset.displayId=String(t),i.style.cssText="position:relative;display:inline-block;margin:4px;background:#000;overflow:hidden;",(this.options.container?.parentElement??document.body).appendChild(i),this.displayConfigs.set(t,{container:i});let o=this.displayConfigs.size>=3?"terminal2d":this.options.renderer??"webgl",a=this.createRenderer(i,o),l=new k(a,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering});if(this.rendererManagers.set(t,l),!this.rendererInitPromises.has(t)){let c=l.initialize(this.core).then(()=>{this.onCorePaletteChanged(this.core.getPalette());let h=this.core.getImageFontIds,u=typeof h=="function"?h.call(this.core):[];u.length>0&&this.onCoreImageFontChanged(u[0]);let m=this.rebuildPostProcessOverlays;typeof m=="function"&&m.call(this)}).catch(h=>{console.warn(`[BaseClientRuntime] Failed to init renderer for display ${t}:`,h)});this.rendererInitPromises.set(t,c)}return l}getRendererType(){return this.rendererType}isRunning(){return this.running}setTickRate(t){if(t<0||t>1e3)throw new Error(`Invalid tick rate: ${t}. Must be between 0 and 1000.`);if(this.tickRate=t,this.userId){let e=this.core.getUser(this.userId);e&&e.setBytesTickRate(t>0?t:20)}t===0&&this.renderMode==="continuous"?(this.renderMode="on-demand",this.log("Tick rate set to 0, switched to on-demand render mode")):this.log(`Tick rate set to ${t} TPS`)}setRenderMode(t){this.renderMode=t,this.log(`Render mode set to ${t}`)}setMaxFPS(t){if(t<=0||t>240)throw new Error(`Invalid FPS: ${t}. Must be between 1 and 240.`);this.FRAME_TIME_MIN=1e3/t,this.log(`Max FPS set to ${t}`)}getTickRate(){return this.tickRate}requestRender(){this.renderMode==="on-demand"&&(this.renderRequested=!0,this.log("Render requested"))}async start(){if(this.running){this.log("Already running");return}this.log("Starting BaseClientRuntime"),await this.onBeforeStart();for(let[t,e]of this.rendererManagers.entries())await e.initialize(this.core),this.log(`Renderer for display ${t} is ready`);this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderers"),this.options.application&&(this.log("Calling application.init()"),await this.options.application.init(this.core,this)),await this.onAfterInit(),await this.createLocalUser(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onBeforeStart(){}async onAfterInit(){}async stop(){if(!this.running)return;this.log("Stopping BaseClientRuntime"),this.running=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),await this.onStop();let t=this.core.getUser(this.userId);t&&this.options.application?.destroyUser&&this.options.application.destroyUser(this.core,t,"Runtime stopped"),this.log("BaseClientRuntime stopped")}async onStop(){}getStats(){let t=this.performanceMonitor.getStats();return{running:this.running,userCount:this.core.getUsers().length,fps:t.fps,uptime:this.running?performance.now()-this.startTime:0,totalFrames:0,lastFrameTiming:t.lastFrame,avgFrameTiming:t.avgFrame}}getTotalBytesReceived(){return this.core.getUser(this.userId)?.getTotalBytesReceived()??0}getBytesReceivedPerSecond(){return this.core.getUser(this.userId)?.getBytesReceivedPerSecond()??0}resetBytesReceived(){let t=this.core.getUser(this.userId);t&&t.resetByteCounters()}async destroy(){await this.stop(),this.log("Destroying BaseClientRuntime"),this.visibilityChangeHandler&&(document.removeEventListener("visibilitychange",this.visibilityChangeHandler),this.visibilityChangeHandler=void 0),await this.onDestroy();for(let t of this.rendererManagers.values()){let e=t.getRenderer();e&&"clearOnResizeCallback"in e&&e.clearOnResizeCallback()}this.options.application?.destroy&&this.options.application.destroy();for(let t of this.rendererManagers.values())t.destroy();this.log("BaseClientRuntime destroyed")}async onDestroy(){}async createLocalUser(){this.userId="local",this.log(`Creating local user: ${this.userId}`);let t=this.core.createUser(this.userId,"User");t.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.onUserCreated(t),this.options.application?.initUser&&(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,t,{username:"User"})),this.processInitialOrders(t);let e=this.core.generateAllLoadPackets();e.forEach(s=>{t.recordBytesReceived(s.length)});let i=this.core.generateMacroLoadPackets(this.userId);i.forEach(s=>{t.recordBytesReceived(s.length)});let r=t.getInputBindingsLoadPacket();r&&t.recordBytesReceived(r.length),this.log(`Simulated ${e.length} load packets, ${i.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach(({static:s,dynamic:o},a)=>{let l=this.core.getUser(a);l&&(s&&l.recordBytesReceived(s.length),o&&l.recordBytesReceived(o.length))})}processInitialOrders(t){if(t.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let e=t.flushMacroOrders();t.applyMacroOrders(e)}}onUserCreated(t){}mainLoop(t){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(t),this.render()),this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}let e=t-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&e<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.lastRenderTimestamp=t;let i=(t-this.lastTimestamp)/1e3,r=Math.min(i,.1);this.lastTimestamp=t,this.performanceMonitor.updateFPS(t);let n=this.core.getUser(this.userId);if(!n){this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}if(this.tickRate===0){let l=performance.now();this.render();let c=performance.now()-l;this.performanceMonitor.recordFrameTiming(0,c),this.rafId=requestAnimationFrame(h=>this.mainLoop(h));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=t,this.accumulatedTime=0,this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.accumulatedTime+=r;let s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let o=0,a=0;for(;this.accumulatedTime>=s&&o<5;){let l=performance.now();this.collectAndApplyInput(n),this.options.application&&(this.options.application.update?.(this.core,s),this.options.application.updateUser?.(this.core,n,s)),this.processTickOrders(n);let c=n.updateMacros();n.processMacroEvents(c),this.core.endTick().forEach(({static:u,dynamic:m})=>{let v=(u?.length??0)+(m?.length??0);n.recordBytesReceived(v)}),a+=performance.now()-l,this.accumulatedTime-=s,o++}if(o>0){let l=performance.now();this.render();let c=performance.now()-l;this.performanceMonitor.recordFrameTiming(a,c)}this.rafId=requestAnimationFrame(l=>this.mainLoop(l))}collectAndApplyInput(t){}processTickOrders(t){if(t.clearTextInputs(),t.hasPendingMacroOrders()){let e=t.flushMacroOrders();t.applyMacroOrders(e)}}render(){let t=this.core.getUser(this.userId);if(t){let i=t.getMacroRenderOrders();for(let[r,n]of i){let s=t.getLayerById(r);s&&n.length>0&&s.addTemporaryOrders(n)}}let e=this.core.getRenderState(this.userId);if(!e||e.displays.length===0){console.warn("[BaseClientRuntime] No render state or displays");return}for(let i of e.displays){let r=this.rendererManagers.get(i.id)??this.ensureRendererManager(i.id);if(!r){console.warn(`[BaseClientRuntime] No renderer manager for display ${i.id}, skipping render`);continue}let n=r.getLayerFilter(),s=n?this.core.getRenderState(this.userId,n):e;if(!s){this.options.debug&&this.log(`Render state unavailable for display ${i.id} with filter`);continue}let o=s.displays.find(a=>a.id===i.id);if(!o){this.options.debug&&this.log(`Render state missing display ${i.id} after filtering`);continue}if(!r.isReady()){this.options.debug&&this.log(`Renderer for display ${i.id} not ready yet`);continue}r.renderDisplay(o)}}onCorePaletteChanged(t){this.log(`Palette changed, updating renderer (${t.size} colors)`);let e=[];for(let n=0;n<256;n++){let s=t.get(n);s?e.push({r:s.r,g:s.g,b:s.b,a:s.a}):e.push({r:0,g:0,b:0,a:0})}let i=R(n=>{n&&"setPalette"in n&&typeof n.setPalette=="function"&&n.setPalette(e)},"applyPalette"),r=0;for(let n of this.rendererManagers.values()){let s=n.getRenderer();s&&(i(s),r++)}r===0?console.warn("[BaseClientRuntime] Cannot update palette: no renderer available"):this.log(`\u2713 Palette updated on ${r} renderer(s)`)}onCoreBitmapFontChanged(t){this.log(`Bitmap font ${t} changed, updating renderer`);let e=this.core.getBitmapFont(t);if(!e){console.warn(`[BaseClientRuntime] Font ${t} not found in registry`);return}let i=new Map,r=0;for(let o=0;o<256;o++){let a=e.getGlyph(o);a&&(i.set(o,a),r++)}this.log(`Built bitmap font map with ${r} glyphs, dimensions: ${e.getCharWidth()}x${e.getCharHeight()}`);let n=R(o=>{o&&"setBitmapFont"in o&&typeof o.setBitmapFont=="function"&&o.setBitmapFont(i,e.getCharWidth(),e.getCharHeight(),e.getCellWidth(),e.getCellHeight())},"applyBitmapFont"),s=0;for(let[o,a]of this.rendererManagers.entries()){let l=a.getRenderer();l&&(n(l),s++,this.onFontChanged(l,o))}s===0?console.warn("[BaseClientRuntime] Cannot update font: no renderer available"):this.log(`\u2713 Renderer bitmap font updated successfully on ${s} renderer(s)`)}onCoreImageFontChanged(t){this.log(`Image font ${t} changed, updating renderer`);let e=this.core.getImageFont(t);if(!e){console.warn(`[BaseClientRuntime] Image font ${t} not found in registry`);return}this.log(`Loading image font with ${e.getAtlasBlocks()} blocks, glyph: ${e.getGlyphWidth()}x${e.getGlyphHeight()}`);let i=R(n=>{n&&"setImageFont"in n&&typeof n.setImageFont=="function"&&n.setImageFont(e.getImageData(),e.getGlyphWidth(),e.getGlyphHeight(),e.getCellWidth(),e.getCellHeight(),e.getAtlasBlocks())},"applyImageFont"),r=0;for(let[n,s]of this.rendererManagers.entries()){let o=s.getRenderer();o&&(i(o),r++,this.onFontChanged(o,n))}r===0?console.warn("[BaseClientRuntime] Cannot update image font: no renderer available"):this.log(`\u2713 Renderer image font updated successfully on ${r} renderer(s)`)}onFontChanged(t,e){}log(t){this.options.debug&&console.warn(`[BaseClientRuntime] ${t}`)}getCore(){return this.core}getRendererManager(t=this.primaryDisplayId){let e=this.rendererManagers.get(t);if(!e)throw new Error(`Renderer manager for display ${t} not found`);return e}setDisplayContainers(t){this.displayConfigs=new Map(t.map(i=>[i.id,{container:i.container}]));let e=Array.from(this.displayConfigs.keys());this.primaryDisplayId=e.length>0?Math.min(...e):0;for(let i of this.rendererManagers.values())i.destroy();this.rendererManagers.clear(),this.createRendererManagers()}getUserId(){return this.userId}};R(G,"BaseClientRuntime");var F=G;import{Core as ce,PostProcessOrderType as P,PostProcessOrderCollector as ue}from"@utsp/core";import{AutoplayOverlay as he,PostProcessOverlay as pe}from"@utsp/render";import{ScalingMode as ge,valueToScalingMode as me,Vector2 as fe}from"@utsp/types";import{UnifiedInputRouter as ye,InputCollector as E,MobileVibration as be}from"@utsp/input";import{SocketIOClient as ve}from"@utsp/network-client";import{AudioManager as Ce}from"@utsp/audio";import{encodeCompressedInput as le}from"@utsp/core";import{InputCollector as U}from"@utsp/input";var w=1e3,z=class z{constructor(t,e,i,r,n,s,o){d(this,"network");d(this,"core");d(this,"rendererManagers");d(this,"primaryDisplayId");d(this,"performanceMonitor");d(this,"options");d(this,"layerTraffic",new Map);d(this,"miscTraffic",new Map);d(this,"displayTraffic",new Map);d(this,"audioManager",null);d(this,"postProcessCallback",null);d(this,"userId","");d(this,"lastReceivedTick",-1);d(this,"connected",!1);d(this,"totalBytesReceived",0);d(this,"lastSentViewports",new Map);d(this,"lastCollectedTouches",[]);d(this,"touchZonesCallback",null);d(this,"audioLoadingState",{totalExpected:0,loadedSounds:new Set,errors:new Map});d(this,"ensureRendererManager");this.network=t,this.core=e,this.rendererManagers=i,this.primaryDisplayId=s,this.performanceMonitor=r,this.ensureRendererManager=o,this.options={serverUrl:n.serverUrl,username:n.username,token:n.token??"",debug:n.debug??!1,autoReconnect:n.autoReconnect??!0,canvasWidth:n.canvasWidth,canvasHeight:n.canvasHeight}}getRendererManager(t){return this.rendererManagers.get(t)}getPrimaryRendererManager(){return this.rendererManagers.get(this.primaryDisplayId)??Array.from(this.rendererManagers.values())[0]}renderDisplays(){let t=this.core.getRenderState(this.userId);if(!t||t.displays.length===0){this.options.debug&&this.log("Render skipped: no render state or displays");return}for(let e of t.displays){let i=this.getRendererManager(e.id)??this.ensureRendererManager?.(e.id);if(!i){console.warn(`[NetworkSync] No renderer manager for display ${e.id}, skipping render`);continue}let r=i.getLayerFilter?.(),n=r?this.core.getRenderState(this.userId,r):t;if(!n){this.options.debug&&this.log(`Render state unavailable for display ${e.id} with filter`);continue}let s=n.displays.find(o=>o.id===e.id);if(!s){this.options.debug&&this.log(`Render state missing display ${e.id} after filtering`);continue}if(!i.isReady()){this.options.debug&&this.log(`Renderer for display ${e.id} not ready yet`);continue}i.renderDisplay(s)}}async connect(){this.log(`Connecting to ${this.options.serverUrl}`),await this.network.connect(),this.connected=!0,this.log("Connected to server"),this.setupNetworkHandlers()}async joinGame(t){return new Promise((e,i)=>{this.network.send("join",{username:this.options.username,token:this.options.token}),this.network.on("join_response",r=>{if(r.success){this.userId=r.userId,this.log(`Joined game as ${this.userId}`);let n=this.core.createUser(this.userId,this.options.username);n.setBytesTickRate(20),this.audioManager&&n.setAudioProcessor(this.audioManager),t?.initUser&&t.initUser(this.core,n,{username:this.options.username,token:this.options.token}),e(this.userId)}else i(new Error(r.error||"Failed to join game"))}),setTimeout(()=>i(new Error("Join timeout")),5e3)})}sendInput(t){let e=this.core.getUser(this.userId);if(!e)return;let i=this.getPrimaryRendererManager(),r=i?.getCanvas();if(r){let g=e.getDisplays(),Z=(g.find(M=>M.getId&&M.getId()===this.primaryDisplayId)??g[0])?.getSize()??{x:this.options.canvasWidth,y:this.options.canvasHeight},J=Z.x,X=Z.y,_=i?.getRenderer()?.getOffsets?.(),ee={offsetX:_?.offsetX??0,offsetY:_?.offsetY??0},A=U.collectMousePosition(t,r,J,X,ee);e.setMousePosition(A.x,A.y,A.over),e.updateMacroMouse(A.x,A.y,A.isLeftDown);let te=U.collectTouchPositions(t,r,J,X,10,ee);te.forEach(M=>{e.setTouchPosition(M.id,M.x,M.y,M.over)}),this.lastCollectedTouches=te}else this.lastCollectedTouches=[];let n=e.getInputBindingRegistry(),s=n.getAllAxes(),o=n.getAllButtons(),a=U.collectAxisSources(s,t),l=U.collectButtonSources(o,t),c=U.collectTextInputs(t);c.length>0&&e.setTextInputs(c);let h=s.sort((g,S)=>g.bindingId-S.bindingId),u=new Map;for(let g of h){let S=n.evaluateAxis(g.bindingId,a);u.set(g.bindingId,S),e.setAxis(g.name,S)}let m=o.sort((g,S)=>g.bindingId-S.bindingId),v=new Map;for(let g of m){let S=n.evaluateButton(g.bindingId,l);v.set(g.bindingId,S),e.setButton(g.name,S)}let f=e.getMouseDisplayInfo(),C=e.getIsMouseOnADisplay(),b=this.collectChangedViewports(),p=this.lastCollectedTouches.map(g=>({id:g.id,x:g.x,y:g.y,over:g.over})),y=le(0n,u,v,f?.displayId??0,f?.localX??0,f?.localY??0,C,c,[],b,p);this.network.send("input",y)}collectChangedViewports(){let t=[];for(let[e,i]of this.rendererManagers.entries()){let{width:r,height:n}=i.getAvailableSize();if(r===0||n===0)continue;let s=this.lastSentViewports.get(e);(!s||s.pixelWidth!==r||s.pixelHeight!==n)&&(t.push({displayId:e,pixelWidth:r,pixelHeight:n}),this.lastSentViewports.set(e,{pixelWidth:r,pixelHeight:n}),this.log(`Viewport changed: display ${e} = ${r}x${n}px`))}return t}disconnect(){this.connected&&(this.network.send("leave",{}),this.network.disconnect(),this.connected=!1,this.log("Disconnected from server"))}getUserId(){return this.userId}isConnected(){return this.connected}getTotalBytesReceived(){return this.totalBytesReceived}getAudioLoadingState(){let t=this.audioLoadingState.loadedSounds.size,e=this.audioLoadingState.totalExpected,i=this.audioLoadingState.errors.size,r=e>0&&t>=e,n=Array.from(this.audioLoadingState.errors.keys());return{loadedCount:t,totalExpected:e,errorCount:i,isComplete:r,errors:n}}destroy(){this.disconnect(),this.network.destroy(),this.log("NetworkSync destroyed")}setupNetworkHandlers(){this.network.on("update",t=>{try{let e=this.convertToUint8Array(t,"update");if(!e)return;this.recordBytesReceived(e.length);let i=this.core.applyUpdatePacketBuffer(this.userId,e);i?(this.postProcessCallback&&i.postProcessOrders&&i.postProcessOrders.length>0&&this.postProcessCallback(i.postProcessOrders),this.renderDisplays()):this.log("Failed to apply update packet")}catch(e){this.log(`Error applying update packet: ${e}`)}}),this.network.on("update-static",t=>{this.handleUpdatePacket(t,"update-static")}),this.network.on("update-dynamic",t=>{this.handleUpdatePacket(t,"update-dynamic")}),this.network.on("load",t=>{try{let e=this.convertToUint8Array(t,"load");if(!e)return;this.recordBytesReceived(e.length),this.core.applyLoadPacket(e)?this.log("Load packet applied successfully"):this.log("Failed to apply load packet")}catch(e){this.log(`Error applying load packet: ${e}`)}}),this.network.on("input-bindings",t=>{if(this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)){this.log("Input bindings configured");try{let i=JSON.parse(t);if(this.log(`Touch zones in packet: ${i.touchZones?.length??0}, callback defined: ${!!this.touchZonesCallback}`),i.touchZones&&i.touchZones.length>0&&this.touchZonesCallback){let r=i.touchZones.map(n=>({id:n.zoneId,x:n.x,y:n.y,width:n.width,height:n.height}));this.touchZonesCallback(r),this.log(`Touch zones configured: ${r.length} zones`)}}catch{}}else this.log("Failed to apply input bindings")}),this.audioManager&&this.setupSoundHandlers(),this.network.on("disconnect",()=>{this.connected=!1,this.log("Disconnected from server")})}handleUpdatePacket(t,e){try{let i=this.convertToUint8Array(t,e);if(!i)return;this.recordBytesReceived(i.length);let r=new DataView(i.buffer,i.byteOffset,i.byteLength),n=Number(r.getBigUint64(0,!1));if(e==="update-dynamic"&&n<this.lastReceivedTick)return;n>this.lastReceivedTick&&(this.lastReceivedTick=n);let s=this.core.applyUpdatePacketBuffer(this.userId,i);if(s){let o=performance.now(),a=R((b,p)=>{if(p<=0)return;let y=this.miscTraffic.get(b)??{total:0,windowSum:0,samples:[]};y.total+=p,y.windowSum+=p,y.samples.push({time:o,bytes:p}),this.miscTraffic.set(b,y)},"recordMisc"),l=R(b=>{try{return JSON.stringify(b).length}catch{return 0}},"safeLen"),c=s.__byteSizes,h=c?.audioOrders;typeof h=="number"?a("update.audioOrders",h):s.audioOrders&&s.audioOrders.length&&a("update.audioOrders",l(s.audioOrders));let u=c?.vibrationOrders;typeof u=="number"?a("update.vibrationOrders",u):s.vibrationOrders&&s.vibrationOrders.length&&a("update.vibrationOrders",l(s.vibrationOrders));let m=c?.macroOrders;typeof m=="number"?a("update.macroOrders",m):s.macroOrders&&s.macroOrders.length&&a("update.macroOrders",l(s.macroOrders));let v=c?.postProcessOrders;if(typeof v=="number"?a("update.postProcessOrders",v):s.postProcessOrders&&s.postProcessOrders.length&&a("update.postProcessOrders",l(s.postProcessOrders)),s.displays&&s.displays.length)for(let b of s.displays){let p=b.id;if(typeof p!="number")continue;let y=this.displayTraffic.get(p)??{total:0,windowSum:0,samples:[]},g=7;y.total+=g,y.windowSum+=g,y.samples.push({time:o,bytes:g}),this.displayTraffic.set(p,y)}for(let b of s.layers){let p=b.byteSize??0,y=this.layerTraffic.get(b.id)??{total:0,windowSum:0,samples:[]};y.total+=p,y.windowSum+=p,y.samples.push({time:o,bytes:p});let g=o-w;for(;y.samples.length&&y.samples[0].time<g;){let S=y.samples.shift();S&&(y.windowSum-=S.bytes)}this.layerTraffic.set(b.id,y)}{let b=o-w;this.miscTraffic.forEach((p,y)=>{for(;p.samples.length&&p.samples[0].time<b;){let g=p.samples.shift();g&&(p.windowSum-=g.bytes)}p.windowSum<0&&(p.windowSum=0),this.miscTraffic.set(y,p)})}{let b=o-w;this.displayTraffic.forEach((p,y)=>{for(;p.samples.length&&p.samples[0].time<b;){let g=p.samples.shift();g&&(p.windowSum-=g.bytes)}p.windowSum<0&&(p.windowSum=0),this.displayTraffic.set(y,p)})}this.postProcessCallback&&s.postProcessOrders&&s.postProcessOrders.length>0&&this.postProcessCallback(s.postProcessOrders),this.options.debug&&this.debugRenderState();let f=performance.now();this.renderDisplays();let C=performance.now()-f;this.performanceMonitor.recordFrameTiming(0,C),this.options.debug&&console.warn(`[CLIENT] render() completed for ${e} (${C.toFixed(2)}ms)`)}else this.options.debug&&this.log(`Failed to apply ${e} packet (tick ${n})`)}catch(i){this.log(`Error applying ${e} update packet: ${i}`),console.error(i)}}convertToUint8Array(t,e){return t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):Array.isArray(t)?new Uint8Array(t):t.data&&Array.isArray(t.data)?new Uint8Array(t.data):typeof t=="object"&&t.type==="Buffer"?new Uint8Array(t.data):(this.log(`Unknown data type for ${e} packet: ${typeof t}`),null)}debugRenderState(){let t=this.core.getUser(this.userId);if(t){let r=t.getLayers();console.warn(`[CLIENT] User has ${r.length} layers`),r.forEach((n,s)=>{let o=n.getOrders(),a=n.getStatic();(o.length>0||a)&&console.warn(` Layer ${s}: ${o.length} orders, static=${a}, z=${n.getZOrder()}`)})}let i=this.getPrimaryRendererManager()?.isReady()??!1;console.warn(`[CLIENT] Renderer ready: ${i}`)}log(t){this.options.debug&&console.warn(`[NetworkSync] ${t}`)}recordBytesReceived(t){this.totalBytesReceived+=t;let e=this.core.getUser(this.userId);e&&e.recordBytesReceived(t)}getLayerTrafficStats(){let t={},i=performance.now()-w;return this.layerTraffic.forEach((r,n)=>{for(;r.samples.length&&r.samples[0].time<i;){let s=r.samples.shift();s&&(r.windowSum-=s.bytes)}r.windowSum<0&&(r.windowSum=0),t[n]={total:r.total,bytesPerSec1s:r.windowSum/(w/1e3)}}),t}getMiscTrafficStats(){let t={},i=performance.now()-w;return this.miscTraffic.forEach((r,n)=>{for(;r.samples.length&&r.samples[0].time<i;){let s=r.samples.shift();s&&(r.windowSum-=s.bytes)}r.windowSum<0&&(r.windowSum=0),t[n]={total:r.total,bytesPerSec1s:r.windowSum/(w/1e3)}}),t}getDisplayTrafficStats(){let t={},i=performance.now()-w;return this.displayTraffic.forEach((r,n)=>{for(;r.samples.length&&r.samples[0].time<i;){let s=r.samples.shift();s&&(r.windowSum-=s.bytes)}r.windowSum<0&&(r.windowSum=0),t[n]={total:r.total,bytesPerSec1s:r.windowSum/(w/1e3)}}),t}getSoundPacketSize(t){return t.mode==="file"?t.sounds.reduce((i,r)=>i+this.getSoundDataLength(r.data)+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,0)}getSoundDataLength(t){if(t instanceof Uint8Array)return t.length;if(t instanceof ArrayBuffer||ArrayBuffer.isView(t))return t.byteLength;let e=globalThis.Buffer;return e&&t instanceof e?t.length:t&&typeof t=="object"&&Array.isArray(t.data)?t.data.length:Array.isArray(t)?t.length:0}setAudioManager(t){if(this.audioManager=t,t&&this.userId){let e=this.core.getUser(this.userId);e&&e.setAudioProcessor(t)}this.connected&&t&&this.setupSoundHandlers()}setPostProcessCallback(t){this.postProcessCallback=t}setTouchZonesCallback(t){this.touchZonesCallback=t}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),t.totalSounds!==void 0&&t.totalSounds>this.audioLoadingState.totalExpected&&(this.audioLoadingState.totalExpected=t.totalSounds,this.log(`Audio loading: expecting ${t.totalSounds} total sounds`)),t.mode==="file"?await this.handleSoundLoad(t):t.mode==="external"&&await this.handleSoundExternalLoad(t)}),this.log("Sound handlers registered"))}async handleSoundLoad(t){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}for(let i of t.sounds)try{let r=this.normalizeSoundData(i.data);await e.loadFromData(i.soundId,i.name,r),this.log(`Loaded sound: ${i.name} (${r.length} bytes)`),this.audioLoadingState.loadedSounds.add(i.name),this.audioLoadingState.errors.delete(i.name),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.audioLoadingState.errors.set(i.name,String(r)),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}normalizeSoundData(t){if(t instanceof Uint8Array)return t;if(t instanceof ArrayBuffer)return new Uint8Array(t);if(ArrayBuffer.isView(t))return new Uint8Array(t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength));let e=globalThis.Buffer;if(e&&t instanceof e)return new Uint8Array(t);if(t&&typeof t=="object"&&Array.isArray(t.data))return new Uint8Array(t.data);if(Array.isArray(t))return new Uint8Array(t);throw new Error("Invalid sound data payload")}async handleSoundExternalLoad(t){if(!this.audioManager){this.log("Cannot load external sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load external sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromUrl(i.soundId,i.name,i.url),this.log(`Loaded external sound: ${i.name} from ${i.url}`),this.audioLoadingState.loadedSounds.add(i.name),this.audioLoadingState.errors.delete(i.name),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,r),this.audioLoadingState.errors.set(i.name,String(r)),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}sendAudioAck(t){this.network.send("audio-ack",t),this.log(`Sent audio-ack: ${t.type}`)}sendBridge(t,e){this.network.sendBridge(t,e),this.log(`Bridge sent on channel '${t}'`)}onBridge(t,e){this.network.onBridge(t,e),this.log(`Bridge handler registered for channel '${t}'`)}offBridge(t,e){this.network.offBridge(t,e),this.log(`Bridge handler removed for channel '${t}'`)}removeAllBridgeHandlers(t){this.network.removeAllBridgeHandlers(t),this.log(t?`All bridge handlers removed for '${t}'`:"All bridge handlers removed")}};R(z,"NetworkSync");var D=z;var W=class W extends F{constructor(e){super({application:e.application,container:e.container,displayContainers:e.displayContainers,debug:e.debug,width:e.width,height:e.height,renderer:e.renderer,useImageDataRendering:e.useImageDataRendering,renderMode:e.renderMode});d(this,"input",null);d(this,"networkSync",null);d(this,"mode");d(this,"localOptions",null);d(this,"autoplayOverlay",null);d(this,"autoplay",!0);d(this,"audioManager",null);d(this,"mobileVibration",null);d(this,"postProcessOverlay",null);d(this,"postProcessOverlays",new Map);d(this,"postProcessOrderCollector",new ue);d(this,"pendingPaletteSlotByDisplay",new Map);d(this,"localBridgeHandlers",new Map);d(this,"localBridgeWildcardHandlers",new Set);if(this.mode=e.mode,e.mode==="local"?(this.localOptions={mode:"local",application:e.application,container:e.container,debug:e.debug??!1,width:e.width??80,height:e.height??25,userId:e.userId??"local",username:e.username??"User",renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,touchZones:e.touchZones,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.userId=e.userId??"local",this.autoplay=e.autoplay??!0):(this.localOptions={mode:"connected",application:e.application,container:e.container,serverUrl:e.serverUrl,username:e.username??"Player",debug:e.debug??!1,width:e.width??80,height:e.height??25,autoReconnect:e.autoReconnect??!0,token:e.token,renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,touchZones:e.touchZones,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.autoplay=e.autoplay??!0),this.log(`Initializing ClientRuntime (${this.mode} mode)`),this.mode==="connected"&&(this.core=new ce({mode:"client",maxUsers:100}),this.core.onPaletteChanged(r=>{this.onCorePaletteChanged(r),this.retryPendingPaletteSelections()}),this.core.onBitmapFontChanged(r=>{this.onCoreBitmapFontChanged(r)}),this.core.onImageFontChanged(r=>{this.onCoreImageFontChanged(r)})),e.inputEnabled??!0?this.input=new ye({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:e.touchZones?.targetElement??void 0,debug:e.debug,keyboardConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mouseConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mobileConfig:{preventDefault:e.mobileInputConfig?.preventDefault??!1,passive:e.mobileInputConfig?.passive??!0,maxTouches:e.mobileInputConfig?.maxTouches??10},enableTouchZones:e.touchZones?.enable??!1,touchZoneConfig:{targetElement:e.touchZones?.targetElement,gridWidth:e.touchZones?.gridWidth??e.width??0,gridHeight:e.touchZones?.gridHeight??e.height??0,rendererOffsets:e.touchZones?.rendererOffsets,zones:e.touchZones?.zones}}):this.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new ve({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:e.debug});this.networkSync=new D(n,this.core,this.rendererManagers,this.performanceMonitor,{serverUrl:r.serverUrl,username:r.username,token:r.token,debug:e.debug,autoReconnect:r.autoReconnect,canvasWidth:e.width??80,canvasHeight:e.height??25},this.primaryDisplayId,s=>this.ensureRendererManager(s))}this.log("ClientRuntime initialized")}rebuildPostProcessOverlays(){for(let e of this.postProcessOverlays.values())e.destroy();this.postProcessOverlays.clear(),this.postProcessOverlay=null;for(let[e,i]of this.displayConfigs.entries()){let r=new pe(i.container);this.postProcessOverlays.set(e,r),e===this.primaryDisplayId&&(this.postProcessOverlay=r)}!this.postProcessOverlay&&this.postProcessOverlays.size>0&&(this.postProcessOverlay=Array.from(this.postProcessOverlays.values())[0]??null)}getMode(){return this.mode}setTickRate(e){if(this.mode==="connected"){this.log("setTickRate() has no effect in connected mode");return}super.setTickRate(e)}async onBeforeStart(){this.audioManager=new Ce({debug:this.options.debug}),this.mobileVibration=new be({debug:this.options.debug}),this.autoplay?(this.log("Autoplay enabled, initializing AudioManager..."),this.audioManager.initialize()):(this.log("Autoplay disabled, showing overlay..."),await new Promise(e=>{this.autoplayOverlay=new he(this.options.container,{...this.localOptions?.autoplayOptions,onStart:()=>{this.log("User clicked start button"),this.audioManager&&(this.audioManager.initialize(),this.audioManager.playStartSound()),this.mobileVibration&&this.mobileVibration.vibrate(50),e()}})}),this.log("Autoplay overlay dismissed, continuing startup..."))}async onAfterInit(){this.rebuildPostProcessOverlays(),this.log("PostProcessOverlays created");let e=this.rendererManager.getCanvas();if(e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync){if(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),this.input){let r=this.input,n=this.rendererManager;this.networkSync.setTouchZonesCallback(s=>{this.log(`[onAfterInit] Touch zones callback called with ${s.length} zones`);let o=n.getCanvas();if(this.log(`[onAfterInit] Canvas available: ${!!o}`),o){let a=s.reduce((h,u)=>Math.max(h,u.x+u.width),0),l=s.reduce((h,u)=>Math.max(h,u.y+u.height),0);this.log(`[onAfterInit] Grid from zones: ${a}x${l}`);let c=r.enableTouchZonesDevice(o,a,l,s);this.log(`[onAfterInit] enableTouchZonesDevice returned: ${c}`),c&&this.log(`Touch zones configured from server: ${s.length} zones`)}})}if(await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application),this.input){let n=this.core.getUser(this.userId)?.getDisplays()??[];if(n.length>0){let s=n[0].getSize();this.input.updateTouchZoneLayout(s.x,s.y)}}let i=this.core.getUser(this.userId);i&&this.configureUserProcessors(i)}}async start(){if(this.running){this.log("Already running");return}this.log("Starting ClientRuntime"),await this.onBeforeStart();for(let[r,n]of this.rendererManagers.entries())await n.initialize(this.core),this.log(`Renderer for display ${r} is ready`);this.rebuildPostProcessOverlays(),this.log("PostProcessOverlays created"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.options.application&&(this.log("Calling application.init()"),await this.options.application.init(this.core,this));let e=this.rendererManager.getCanvas();if(e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync){if(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(n=>{this.applyPostProcessOrders(n)}),this.input){let n=this.input,s=this.rendererManager;this.networkSync.setTouchZonesCallback(o=>{this.log(`[start] Touch zones callback called with ${o.length} zones`);let a=s.getCanvas();if(this.log(`[start] Canvas available: ${!!a}`),a){let l=o.reduce((u,m)=>Math.max(u,m.x+m.width),0),c=o.reduce((u,m)=>Math.max(u,m.y+m.height),0);this.log(`[start] Grid from zones: ${l}x${c}`);let h=n.enableTouchZonesDevice(a,l,c,o);this.log(`[start] enableTouchZonesDevice returned: ${h}`),h&&this.log(`Touch zones configured from server: ${o.length} zones`)}})}await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application);let r=this.core.getUser(this.userId);r&&this.configureUserProcessors(r)}else await this.createLocalUser();let i=this.core.getUser(this.userId);if(i){let r=i.getDisplays();if(r.length>0){let s=r[0].getSize(),o=s.x,a=s.y,l=this.rendererManager.getRenderer(),c,h;if(this.rendererType==="webgl"){let m=l.getGridSize();c=m.cols,h=m.rows}else{let u=l;c=u.getCols(),h=u.getRows()}(c!==o||h!==a)&&(this.log(`Adjusting renderer from ${c}\xD7${h} to match display ${o}\xD7${a}`),l.resize(o,a))}}this.input&&this.input.start(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onStop(){this.audioManager&&this.audioManager.stopAll(),this.input&&this.input.stop(),this.mode==="connected"&&this.networkSync&&this.networkSync.disconnect()}getStats(){let e=super.getStats(),r=this.core.getUser(this.userId)?.getTotalBytesReceived();return{...e,mode:this.mode,latency:void 0,totalBytesReceived:r}}getAudioLoadingState(){if(this.mode==="connected"&&this.networkSync)return this.networkSync.getAudioLoadingState();if(this.audioManager){let e=this.audioManager.getSoundBank();if(e){let i=e.getStats();return{loadedCount:i.totalSounds,totalExpected:i.totalSounds,errorCount:0,isComplete:!0,errors:[]}}}return{loadedCount:0,totalExpected:0,errorCount:0,isComplete:!0,errors:[]}}async onDestroy(){this.autoplayOverlay&&(this.autoplayOverlay.destroy(),this.autoplayOverlay=null),this.audioManager&&(this.audioManager.destroy(),this.audioManager=null),this.postProcessOverlay&&(this.postProcessOverlay.destroy(),this.postProcessOverlay=null);for(let e of this.postProcessOverlays.values())e.destroy();this.postProcessOverlays.clear(),this.input&&this.input.destroy(),this.networkSync&&this.networkSync.destroy()}configureUserProcessors(e){if(this.audioManager&&e.setAudioProcessor(this.audioManager),this.mobileVibration&&e.setMobileVibrationProcessor(this.mobileVibration),this.input){let i=this.input.getGamepad();i&&e.setGamepadVibrationProcessor(i)}}async createLocalUser(){let e=this.localOptions;this.userId=e?.userId??"local",this.log(`Creating local user: ${this.userId}`);let i=this.core.createUser(this.userId,e?.username??"User");if(i.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.configureUserProcessors(i),this.options.application?.initUser&&(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,i,{username:e?.username??"User"})),i.hasAudioConfigCommands()&&(this.log("Applying audio config commands from initUser()"),i.applyAudioConfigCommands(i.flushAudioConfigCommands())),i.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let a=i.flushMacroOrders();i.applyMacroOrders(a)}if(i.hasPostProcessCommands()){this.log("Applying post-process commands from initUser()");let a=i.flushPostProcessCommands(),l=this.postProcessOrderCollector.convertCommands(a);this.applyPostProcessOrders(l)}i.needsSendSounds()&&(await this.loadSoundsFromRegistry(),i.clearSendSounds());let r=this.core.generateAllLoadPackets();r.forEach(a=>{i.recordBytesReceived(a.length)});let n=this.core.generateMacroLoadPackets(this.userId);n.forEach(a=>{i.recordBytesReceived(a.length)});let s=i.getInputBindingsLoadPacket();if(s&&(i.recordBytesReceived(s.length),this.input)){let l=i.getInputBindingRegistry().getAllTouchZones();if(l.length>0){let c=this.rendererManager.getCanvas();if(c){let h=i.getDisplays(),u=h.length>0?h[0].getSize():{x:this.options.width??80,y:this.options.height??25},m=l.map(f=>({id:f.zoneId,x:f.x,y:f.y,width:f.width,height:f.height}));this.input.enableTouchZonesDevice(c,u.x,u.y,m)?this.log(`Touch zones synchronized from registry: ${l.length} zones`):this.log("Failed to enable touch zones - mobile input may not be initialized")}}}this.log(`Simulated ${r.length} load packets, ${n.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach(({static:a,dynamic:l},c)=>{let h=this.core.getUser(c);h&&(a&&h.recordBytesReceived(a.length),l&&h.recordBytesReceived(l.length))}),this.render(),this.log("Initial render complete")}async loadSoundsFromRegistry(){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=this.core.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry into AudioManager...`);let n=this.core.getUser(this.userId),s=0;for(let o of r)try{if(o.loadType==="file"&&o.data){await e.loadFromData(o.soundId,o.name,o.data);let a=o.data.length+o.name.length+10;s+=a,this.log(`\u{1F50A} Loaded sound: ${o.name} (${o.data.length} bytes)`)}else if(o.loadType==="external"&&o.url){await e.loadFromUrl(o.soundId,o.name,o.url);let a=o.url.length+o.name.length+10;s+=a,this.log(`\u{1F50A} Loaded external sound: ${o.name} from ${o.url}`)}}catch(a){console.error(`[ClientRuntime] Failed to load sound "${o.name}":`,a)}n&&s>0&&(n.recordBytesReceived(s),this.log(`Simulated ${s} bytes for ${r.length} sound(s)`)),this.log(`\u2713 ${e.size} sounds loaded into AudioManager`)}mainLoop(e){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(e),this.render()),this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}let i=e-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&i<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}this.lastRenderTimestamp=e;let r=(e-this.lastTimestamp)/1e3,n=Math.min(r,.1);this.lastTimestamp=e,this.performanceMonitor.updateFPS(e);let s=this.core.getUser(this.userId);if(!s){this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}if(this.mode==="local"){if(this.tickRate===0){let c=performance.now();this.render();let h=performance.now()-c;this.performanceMonitor.recordFrameTiming(0,h),this.rafId=requestAnimationFrame(u=>this.mainLoop(u));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=e,this.accumulatedTime=0,this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.accumulatedTime+=n;let o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let a=0,l=0;for(;this.accumulatedTime>=o&&a<5;){let c=performance.now();if(this.collectAndApplyInput(s),this.options.application&&(this.options.application.update?.(this.core,o),this.options.application.updateUser?.(this.core,s,o)),s.clearTextInputs(),s.hasAudioConfigCommands()&&s.applyAudioConfigCommands(s.flushAudioConfigCommands()),s.hasSoundCommands()&&s.applyAudioCommands(s.flushSoundCommands()),s.hasMobileVibrationCommands()&&s.applyMobileVibrationCommands(s.flushMobileVibrationCommands()),s.hasGamepadVibrationCommands()&&s.applyGamepadVibrationCommands(s.flushGamepadVibrationCommands()),s.hasPostProcessCommands()){let m=s.flushPostProcessCommands(),v=this.postProcessOrderCollector.convertCommands(m);this.applyPostProcessOrders(v)}if(s.hasPendingMacroOrders()){let m=s.flushMacroOrders();s.applyMacroOrders(m)}let h=s.updateMacros();if(s.processMacroEvents(h),s.hasBridgeMessages()){let m=s.getBridgeMessages();this.dispatchLocalBridgeMessages(m)}this.core.endTick().forEach(({static:m,dynamic:v})=>{let f=(m?.length??0)+(v?.length??0);s.recordBytesReceived(f)}),l+=performance.now()-c,this.accumulatedTime-=o,a++}if(a>0){let c=performance.now();this.render();let h=performance.now()-c;this.performanceMonitor.recordFrameTiming(l,h)}}else{let o=s.updateMacros();s.processMacroEvents(o),this.collectAndSendInput()}this.rafId=requestAnimationFrame(o=>this.mainLoop(o))}collectAndApplyInput(e){if(!this.input)return;let i=this.rendererManager.getCanvas();if(i){let a=e.getDisplays(),c=(a.find(g=>g.getId&&g.getId()===this.primaryDisplayId)??a[0])?.getSize()??{x:this.options.width,y:this.options.height},h=c.x,u=c.y,v=this.rendererManager.getRenderer()?.getOffsets?.(),f={offsetX:v?.offsetX??0,offsetY:v?.offsetY??0};this.input.updateTouchZoneLayout(h,u,f);let C=E.collectMousePosition(this.input,i,h,u,f);e.setMousePosition(C.x,C.y,C.over),e.updateMacroMouse(C.x,C.y,C.isLeftDown),E.collectTouchPositions(this.input,i,h,u,10,f).forEach(g=>{e.setTouchPosition(g.id,g.x,g.y,g.over)});let y=this.rendererManagers.get(this.primaryDisplayId)?.getAvailableSize();y&&e.setDisplayViewport(this.primaryDisplayId,y.width,y.height)}let r=E.collectTextInputs(this.input);r.length>0&&e.setTextInputs(r);let n=e.getInputBindingRegistry(),s=n.getAllAxes(),o=n.getAllButtons();if(s.length>0||o.length>0){let a=E.collectAxisSources(s,this.input),l=E.collectButtonSourcesWithTransitions(o,this.input);s.forEach(c=>{let h=n.evaluateAxis(c.bindingId,a);e.setAxis(c.name,h)}),o.forEach(c=>{let h=new Map,u=new Map,m=new Map;for(let[b,p]of l)h.set(b,p.pressed),u.set(b,p.justPressed),m.set(b,p.justReleased);let v=n.evaluateButton(c.bindingId,h),f=n.evaluateButton(c.bindingId,u),C=n.evaluateButton(c.bindingId,m);e.setButton(c.name,v),e.setButton(`${c.name}_justPressed`,f),e.setButton(`${c.name}_justReleased`,C)})}this.input.poll?.()}collectAndSendInput(){!this.networkSync||!this.input||this.networkSync.sendInput(this.input)}render(){let e=this.core.getUser(this.userId);if(e){let i=e.getDisplays();for(let r of i){let n=r.getId(),s=this.rendererManagers.get(n);if(!s){console.warn(`[ClientRuntime] No renderer for display ${n}, skipping resize`);continue}let o=s.getRenderer();if(!o)continue;let a=r.getSize(),l,c,h,u,m=null;if("getGridSize"in o){let f=o,C=f.getGridSize();l=C.cols,c=C.rows,h=f.getCellWidth(),u=f.getCellHeight(),m=f.getScalingMode()}else if("getCols"in o){let f=o;l=f.getCols(),c=f.getRows(),h=f.getCellWidth(),u=f.getCellHeight(),m=f.getScalingMode()}else continue;if(m===ge.Responsive){let f=s.getAvailableSize(),C=256*256;"getMaxCells"in o&&(C=Math.min(C,o.getMaxCells()));let b=Math.floor(f.width/h),p=Math.floor(f.height/u);if(b=Math.min(256,Math.max(1,b)),p=Math.min(256,Math.max(1,p)),b*p>C){let y=Math.sqrt(C/(b*p));b=Math.max(1,Math.floor(b*y)),p=Math.max(1,Math.floor(p*y)),this.log(`\u26A0\uFE0F [Display ${n}] Device limit: max ${C} cells, clamping to ${b}\xD7${p}`)}(a.x!==b||a.y!==p)&&(this.log(`\u{1F4D0} [Display ${n}] Resizing display from ${a.x}\xD7${a.y} to ${b}\xD7${p} (available: ${f.width}\xD7${f.height}px, cell: ${h}\xD7${u}px)`),r.setSize(new fe(b,p)))}let v=r.getSize();(l!==v.x||c!==v.y)&&(this.log(`\u{1F4D0} [Display ${n}] Resizing renderer from ${l}\xD7${c} to ${v.x}\xD7${v.y}`),o.resize(v.x,v.y))}}super.render()}onFontChanged(e,i){let r=i??this.primaryDisplayId,n=this.postProcessOverlays.get(r)??this.postProcessOverlay;n&&"syncWithRenderer"in n&&"getCellWidth"in e&&"getCellHeight"in e&&"getCurrentScale"in e&&"getGridSize"in e&&(n.syncWithRenderer(e),this.log(`\u2713 PostProcessOverlay synced with renderer dimensions (display ${r})`))}getAudioManager(){return this.audioManager}getLayerTrafficStats(){return this.networkSync?.getLayerTrafficStats()??{}}getMiscTrafficStats(){return this.networkSync?.getMiscTrafficStats?.()??{}}getDisplayTrafficStats(){return this.networkSync?.getDisplayTrafficStats?.()??{}}getAudioContext(){return this.audioManager?.getContext()??null}getPostProcessOverlay(){return this.postProcessOverlay}applyAmbientEffectConfig(e,i){let r=this.rendererManagers.get(e)?.getRenderer();r&&"setAmbientEffect"in r&&(i.enabled?r.setAmbientEffect({blur:i.blur,scale:i.scale}):r.setAmbientEffect(!1),this.log(`[Display ${e}] Ambient effect ${i.enabled?"enabled":"disabled"}`))}applyPostProcessOrders(e){for(let i of e){let r=i.displayId??this.primaryDisplayId,n=this.postProcessOverlays.get(r)??this.postProcessOverlay,s=this.rendererManagers.get(r)?.getRenderer();switch(i.type){case P.SetConfig:{if(!n){console.warn(`[ClientRuntime] No PostProcessOverlay for display ${r}`);break}let o={};i.scanlines&&(o.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(o.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),n.setConfig(Object.keys(o).length>0?o:null),o.ambientEffect&&s&&this.applyAmbientEffectConfig(r,o.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case P.SetScanlines:{if(!n){console.warn(`[ClientRuntime] No PostProcessOverlay for display ${r}`);break}n.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case P.SetAmbientEffect:{this.applyAmbientEffectConfig(r,{enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case P.SetScalingMode:{this.applyScalingMode(r,i.mode);break}case P.SetGrid:{this.applyGridConfig(r,i);break}case P.SwitchPalette:{this.applySwitchPalette(r,i);break}case P.SetCellSize:{this.applyCellSize(r,i.cellWidth,i.cellHeight);break}}}}applyScalingMode(e,i){let r=this.rendererManagers.get(e)?.getRenderer();if(r&&"setScalingMode"in r){let n=me(i);r.setScalingMode(n),this.log(`[Display ${e}] Scaling mode set to ${n}`)}}applyGridConfig(e,i){let r=this.rendererManagers.get(e)?.getRenderer();if(r&&"setGrid"in r){let n=i.colorA/255;r.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${n})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}applySwitchPalette(e,i){let r=this.core.getPaletteFromSlot(i.slotId);if(!r){this.pendingPaletteSlotByDisplay.set(e,i.slotId),console.warn(`[ClientRuntime] Palette slot ${i.slotId} not found (display ${e}); will retry when available`);return}let n=[];for(let o=0;o<256;o++){let a=r.get(o);a?n.push({r:a.r,g:a.g,b:a.b,a:a.a}):n.push({r:0,g:0,b:0,a:0})}let s=this.rendererManagers.get(e)?.getRenderer();s&&"setPalette"in s&&(s.setPalette(n),this.log(`[Display ${e}] Switched to palette slot ${i.slotId}`)),this.pendingPaletteSlotByDisplay.get(e)===i.slotId&&this.pendingPaletteSlotByDisplay.delete(e)}retryPendingPaletteSelections(){if(this.pendingPaletteSlotByDisplay.size!==0)for(let[e,i]of this.pendingPaletteSlotByDisplay.entries()){let r=this.core.getPaletteFromSlot(i);if(!r)continue;let n=[];for(let o=0;o<256;o++){let a=r.get(o);a?n.push({r:a.r,g:a.g,b:a.b,a:a.a}):n.push({r:0,g:0,b:0,a:0})}let s=this.rendererManagers.get(e)?.getRenderer();s&&"setPalette"in s&&(s.setPalette(n),this.log(`[Display ${e}] Applied pending palette slot ${i}`),this.pendingPaletteSlotByDisplay.delete(e))}}applyCellSize(e,i,r){let n=this.rendererManagers.get(e)?.getRenderer();n&&"setCellSize"in n&&(n.setCellSize(i,r),this.log(`[Display ${e}] Cell size set to ${i}\xD7${r}px`))}patternFromType(e){switch(e){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}dispatchLocalBridgeMessages(e){for(let i of e){let{channel:r,data:n}=i,s=this.localBridgeHandlers.get(r);if(s)for(let o of s)try{o(n)}catch(a){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,a)}for(let o of this.localBridgeWildcardHandlers)try{o(r,n)}catch(a){console.error("[ClientRuntime] Bridge wildcard handler error:",a)}this.log(`Bridge message dispatched on channel '${r}'`)}}sendBridge(e,i){if(this.mode==="connected"&&this.networkSync)this.networkSync.sendBridge(e,i);else if(this.mode==="local"){let r=this.core.getUser(this.userId);r&&this.options.application?.onBridgeMessage&&(this.options.application.onBridgeMessage(this.core,r,e,i),this.log(`Bridge message sent to application on channel '${e}'`))}}onBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.onBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.add(i):(this.localBridgeHandlers.has(e)||this.localBridgeHandlers.set(e,new Set),this.localBridgeHandlers.get(e).add(i)),this.log(`Bridge handler registered for channel '${e}'`))}offBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.offBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.delete(i):this.localBridgeHandlers.get(e)?.delete(i))}removeAllBridgeHandlers(e){this.mode==="connected"&&this.networkSync?this.networkSync.removeAllBridgeHandlers(e):this.mode==="local"&&(e===void 0?(this.localBridgeHandlers.clear(),this.localBridgeWildcardHandlers.clear()):e==="*"?this.localBridgeWildcardHandlers.clear():this.localBridgeHandlers.delete(e))}log(e){this.options.debug&&console.warn(`[ClientRuntime/${this.mode}] ${e}`)}};R(W,"ClientRuntime");var q=W;import{UnifiedInputRouter as Re,InputCollector as O,MobileVibration as Se}from"@utsp/input";var Q=class Q{constructor(t={}){d(this,"input");d(this,"mobileVibration");this.input=new Re({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:t.debug,keyboardConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mouseConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mobileConfig:{preventDefault:t.mobileInputConfig?.preventDefault??!1,passive:t.mobileInputConfig?.passive??!0,maxTouches:t.mobileInputConfig?.maxTouches??10}}),this.mobileVibration=new Se({debug:t.debug})}setMobileTarget(t){this.input.setMobileTarget(t)}start(){this.input.start()}stop(){this.input.stop()}destroy(){this.input.destroy()}getRouter(){return this.input}getGamepad(){return this.input.getGamepad()}getMobileVibration(){return this.mobileVibration}collectAndApply(t,e,i,r,n){if(e){let c=O.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(c.x,c.y,c.over),t.updateMacroMouse(c.x,c.y,c.isLeftDown),O.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let s=O.collectTextInputs(this.input);s.length>0&&t.setTextInputs(s);let o=t.getInputBindingRegistry(),a=o.getAllAxes(),l=o.getAllButtons();if(a.length>0||l.length>0){let c=O.collectAxisSources(a,this.input),h=O.collectButtonSourcesWithTransitions(l,this.input);a.forEach(u=>{let m=o.evaluateAxis(u.bindingId,c);t.setAxis(u.name,m)}),l.forEach(u=>{let m=new Map,v=new Map,f=new Map;for(let[y,g]of h)m.set(y,g.pressed),v.set(y,g.justPressed),f.set(y,g.justReleased);let C=o.evaluateButton(u.bindingId,m),b=o.evaluateButton(u.bindingId,v),p=o.evaluateButton(u.bindingId,f);t.setButton(u.name,C),t.setButton(`${u.name}_justPressed`,b),t.setButton(`${u.name}_justReleased`,p)})}this.input.poll?.()}processVibrationCommands(t){t.hasMobileVibrationCommands()&&t.applyMobileVibrationCommands(t.flushMobileVibrationCommands()),t.hasGamepadVibrationCommands()&&t.applyGamepadVibrationCommands(t.flushGamepadVibrationCommands())}};R(Q,"InputFeature");var H=Q;import{AudioManager as Ie}from"@utsp/audio";var Y=class Y{constructor(t={}){d(this,"audioManager");d(this,"debug");this.debug=t.debug??!1,this.audioManager=new Ie({debug:t.debug})}initialize(){this.audioManager.initialize()}playStartSound(){this.audioManager.playStartSound()}stopAll(){this.audioManager.stopAll()}destroy(){this.audioManager.destroy()}getManager(){return this.audioManager}getContext(){return this.audioManager.getContext()}injectIntoUser(t){t.setAudioProcessor(this.audioManager)}processAudioCommands(t){t.hasAudioConfigCommands()&&t.applyAudioConfigCommands(t.flushAudioConfigCommands()),t.hasSoundCommands()&&t.applyAudioCommands(t.flushSoundCommands())}async loadSoundsFromRegistry(t){let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=t.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry...`);for(let n of r)try{n.loadType==="file"&&n.data?(await e.loadFromData(n.soundId,n.name,n.data),this.log(`Loaded sound: ${n.name} (${n.data.length} bytes)`)):n.loadType==="external"&&n.url&&(await e.loadFromUrl(n.soundId,n.name,n.url),this.log(`Loaded external sound: ${n.name} from ${n.url}`))}catch(s){console.error(`[AudioFeature] Failed to load sound "${n.name}":`,s)}this.log(`${e.size} sounds loaded`)}log(t){this.debug&&console.warn(`[AudioFeature] ${t}`)}};R(Y,"AudioFeature");var K=Y;import{PostProcessOrderCollector as we,PostProcessOrderType as x}from"@utsp/core";import{PostProcessOverlay as Me}from"@utsp/render";import{valueToScalingMode as Te}from"@utsp/types";var N=class N{constructor(t,e={}){d(this,"overlay");d(this,"orderCollector");d(this,"debug");this.debug=e.debug??!1,this.overlay=new Me(t),this.orderCollector=new we}destroy(){this.overlay.destroy()}getOverlay(){return this.overlay}syncWithRenderer(t){this.overlay.syncWithRenderer(t),this.log("Overlay synced with renderer dimensions")}processCommands(t,e){if(!t.hasPostProcessCommands())return;let i=t.flushPostProcessCommands(),r=this.orderCollector.convertCommands(i);this.applyOrders(r,e)}applyOrders(t,e){for(let i of t){let r=i.displayId;switch(i.type){case x.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.overlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffect(e,n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case x.SetScanlines:{this.overlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case x.SetAmbientEffect:{this.applyAmbientEffect(e,{enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case x.SetScalingMode:{this.applyScalingMode(e,r,i.mode);break}case x.SetGrid:{this.applyGridConfig(e,r,i);break}}}}applyAmbientEffect(t,e){t&&"setAmbientEffect"in t&&(e.enabled?t.setAmbientEffect({blur:e.blur,scale:e.scale}):t.setAmbientEffect(!1))}applyScalingMode(t,e,i){if(t&&"setScalingMode"in t){let r=Te(i);t.setScalingMode(r),this.log(`[Display ${e}] Scaling mode set to ${r}`)}}applyGridConfig(t,e,i){if(t&&"setGrid"in t){let r=i.colorA/255;t.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${r})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}patternFromType(t){switch(t){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}log(t){this.debug&&console.warn(`[PostProcessFeature] ${t}`)}};R(N,"PostProcessFeature");var j=N;import{ScalingMode as Tt}from"@utsp/render";import{AudioManager as Ft}from"@utsp/audio";export{K as AudioFeature,Ft as AudioManager,F as BaseClientRuntime,q as ClientRuntime,T as DEFAULT_ATLAS_CONFIG,H as InputFeature,j as PostProcessFeature,k as RendererManager,re as RendererType,Tt as ScalingMode,$ as decodeDefaultAtlas};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utsp/runtime-client",
|
|
3
|
-
"version": "0.15.0
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "UTSP Runtime Client - Local and multi-user client runtime",
|
|
5
5
|
"author": "THP Software",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@utsp/audio": "0.15.0
|
|
52
|
-
"@utsp/core": "0.15.0
|
|
53
|
-
"@utsp/input": "0.15.0
|
|
54
|
-
"@utsp/network-client": "0.15.0
|
|
55
|
-
"@utsp/render": "0.15.0
|
|
56
|
-
"@utsp/types": "0.15.0
|
|
51
|
+
"@utsp/audio": "0.15.0",
|
|
52
|
+
"@utsp/core": "0.15.0",
|
|
53
|
+
"@utsp/input": "0.15.0",
|
|
54
|
+
"@utsp/network-client": "0.15.0",
|
|
55
|
+
"@utsp/render": "0.15.0",
|
|
56
|
+
"@utsp/types": "0.15.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"typescript": "^5.6.3"
|