@utsp/runtime-client 0.14.1 → 0.15.0-nightly.20251227200128.cdc09ed

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 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,t,e)=>t in y?E(y,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):y[t]=e;var b=(y,t)=>E(y,"name",{value:t,configurable:!0});var ye=(y,t)=>{for(var e in t)E(y,e,{get:t[e],enumerable:!0})},be=(y,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ge(t))!me.call(y,r)&&r!==e&&E(y,r,{get:()=>t[r],enumerable:!(i=pe(t,r))||i.enumerable});return y};var ve=y=>be(E({},"__esModule",{value:!0}),y);var a=(y,t,e)=>(fe(y,typeof t!="symbol"?t+"":t,e),e);var Ie={};ye(Ie,{AudioFeature:()=>q,AudioManager:()=>ue.AudioManager,BaseClientRuntime:()=>A,ClientRuntime:()=>D,InputFeature:()=>G,PostProcessFeature:()=>W,RendererType:()=>Q,ScalingMode:()=>he.ScalingMode});module.exports=ve(Ie);var ne=require("@utsp/core"),V=require("@utsp/render");var Q=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(Q||{});var H=class H{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}};b(H,"PerformanceMonitor");var O=H;var Ce="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",P={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function re(){let y=atob(Ce),t=new Uint8Array(y.length);for(let e=0;e<y.length;e++)t[e]=y.charCodeAt(e);return t}b(re,"decodeDefaultAtlas");var z=class z{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=re();await this.renderer.setImageFont(e,P.glyphWidth,P.glyphHeight,P.cellWidth,P.cellHeight,P.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=b(()=>{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(s,e)},"check");s()})}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}`)}};b(z,"RendererManager");var L=z;var K=class K{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.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 e=this.createRenderer();this.rendererType=this.options.renderer,this.rendererManager=new L(e,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering}),this.performanceMonitor=new O(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 V.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 V.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(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(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},d)=>{let l=this.core.getUser(d);l&&(s&&l.recordBytesReceived(s.length),o&&l.recordBytesReceived(o.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 s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let o=0,d=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:h})=>{let g=(u?.length??0)+(h?.length??0);n.recordBytesReceived(g)}),d+=performance.now()-l,this.accumulatedTime-=s,o++}if(o>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 s=0;s<256;s++){let o=e.getGlyph(s);o&&(i.set(s,o),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}};b(K,"BaseClientRuntime");var A=K;var S=require("@utsp/core"),B=require("@utsp/render"),U=require("@utsp/types"),T=require("@utsp/input"),oe=require("@utsp/network-client"),ae=require("@utsp/audio");var se=require("@utsp/core"),F=require("@utsp/input");var Y=class Y{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",[]);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(),C=f.length>0?f[0].getSize():{x:this.options.canvasWidth,y:this.options.canvasHeight},_=C.x,Z=C.y,ee=this.rendererManager.getRenderer()?.getOffsets?.(),te={offsetX:ee?.offsetX??0,offsetY:ee?.offsetY??0},k=F.InputCollector.collectMousePosition(t,i,_,Z,te);e.setMousePosition(k.x,k.y,k.over),e.updateMacroMouse(k.x,k.y,k.isLeftDown);let ie=F.InputCollector.collectTouchPositions(t,i,_,Z,10,te);ie.forEach(x=>{e.setTouchPosition(x.id,x.x,x.y,x.over)}),this.lastCollectedTouches=ie}else this.lastCollectedTouches=[];let r=e.getInputBindingRegistry(),n=r.getAllAxes(),s=r.getAllButtons(),o=F.InputCollector.collectAxisSources(n,t),d=F.InputCollector.collectButtonSources(s,t),l=F.InputCollector.collectTextInputs(t);l.length>0&&e.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,o);p.set(f.bindingId,C),e.setAxis(f.name,C)}let u=s.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),e.setButton(f.name,C)}let g=e.getMouseDisplayInfo(),m=e.getIsMouseOnADisplay(),v=this.collectChangedViewports(),I=this.lastCollectedTouches.map(f=>({id:f.id,x:f.x,y:f.y,over:f.over})),R=(0,se.encodeCompressedInput)(0n,p,h,g?.displayId??0,g?.localX??0,g?.localY??0,m,l,[],v,I);this.network.send("input",R)}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}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=>{this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)?this.log("Input bindings configured"):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){this.postProcessCallback&&s.postProcessOrders&&s.postProcessOrders.length>0&&this.postProcessCallback(s.postProcessOrders),this.options.debug&&this.debugRenderState();let o=performance.now();this.rendererManager.render(this.core,this.userId);let d=performance.now()-o;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 s=r.getOrders(),o=r.getStatic();(s.length>0||o)&&console.warn(` Layer ${n}: ${s.length} orders, static=${o}, 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+r.data.length+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,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}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),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{await e.loadFromData(i.soundId,i.name,i.data),this.log(`Loaded sound: ${i.name} (${i.data.length} bytes)`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}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.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,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")}};b(Y,"NetworkSync");var $=Y;var j=class j extends A{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 S.PostProcessOrderCollector);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,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,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 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)})),e.inputEnabled??!0?this.input=new T.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.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new oe.SocketIOClient({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:e.debug});this.networkSync=new $(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 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(e=>{this.autoplayOverlay=new B.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.postProcessOverlay=new B.PostProcessOverlay(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){this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application);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 B.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 e=this.rendererManager.getCanvas();if(e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync){this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(n=>{this.applyPostProcessOrders(n)}),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,d=s.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!==o||p!==d)&&(this.log(`Adjusting renderer from ${c}\xD7${p} to match display ${o}\xD7${d}`),l.resize(o,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}}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 s=i.getInputBindingsLoadPacket();s&&i.recordBytesReceived(s.length),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),s=0;for(let o of r)try{if(o.loadType==="file"&&o.data){await e.loadFromData(o.soundId,o.name,o.data);let d=o.data.length+o.name.length+10;s+=d,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 d=o.url.length+o.name.length+10;s+=d,this.log(`\u{1F50A} Loaded external sound: ${o.name} from ${o.url}`)}}catch(d){console.error(`[ClientRuntime] Failed to load sound "${o.name}":`,d)}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 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 o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let d=0,l=0;for(;this.accumulatedTime>=o&&d<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 h=s.flushPostProcessCommands(),g=this.postProcessOrderCollector.convertCommands(h);this.applyPostProcessOrders(g)}if(s.hasPendingMacroOrders()){let h=s.flushMacroOrders();s.applyMacroOrders(h)}let p=s.updateMacros();if(s.processMacroEvents(p),s.hasBridgeMessages()){let h=s.getBridgeMessages();this.dispatchLocalBridgeMessages(h)}this.core.endTick().forEach(({static:h,dynamic:g})=>{let m=(h?.length??0)+(g?.length??0);s.recordBytesReceived(m)}),l+=performance.now()-c,this.accumulatedTime-=o,d++}if(d>0){let c=performance.now();this.render();let p=performance.now()-c;this.performanceMonitor.recordFrameTiming(l,p)}}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 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?.(),g={offsetX:h?.offsetX??0,offsetY:h?.offsetY??0},m=T.InputCollector.collectMousePosition(this.input,i,c,p,g);e.setMousePosition(m.x,m.y,m.over),e.updateMacroMouse(m.x,m.y,m.isLeftDown),T.InputCollector.collectTouchPositions(this.input,i,c,p,10,g).forEach(R=>{e.setTouchPosition(R.id,R.x,R.y,R.over)});let I=this.rendererManager.getAvailableSize();e.setDisplayViewport(0,I.width,I.height)}let r=T.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 d=T.InputCollector.collectAxisSources(s,this.input),l=T.InputCollector.collectButtonSourcesWithTransitions(o,this.input);s.forEach(c=>{let p=n.evaluateAxis(c.bindingId,d);e.setAxis(c.name,p)}),o.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 g=n.evaluateButton(c.bindingId,p),m=n.evaluateButton(c.bindingId,u),v=n.evaluateButton(c.bindingId,h);e.setButton(c.name,g),e.setButton(`${c.name}_justPressed`,m),e.setButton(`${c.name}_justReleased`,v)})}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(),s=this.rendererManager.getRenderer();if(s){let o,d,l,c,p=null;if(this.rendererType==="webgl"){let h=s,g=h.getGridSize();o=g.cols,d=g.rows,l=h.getCellWidth(),c=h.getCellHeight(),p=h.getScalingMode()}else{let h=s;o=h.getCols(),d=h.getRows(),l=h.getCellWidth(),c=h.getCellHeight(),p=h.getScalingMode()}if(p===U.ScalingMode.Responsive){let h=this.rendererManager.getAvailableSize(),g=256*256;"getMaxCells"in s&&(g=Math.min(g,s.getMaxCells()));let m=Math.floor(h.width/l),v=Math.floor(h.height/c);if(m=Math.min(256,Math.max(1,m)),v=Math.min(256,Math.max(1,v)),m*v>g){let I=Math.sqrt(g/(m*v));m=Math.max(1,Math.floor(m*I)),v=Math.max(1,Math.floor(v*I)),this.log(`\u26A0\uFE0F Device limit: max ${g} cells, clamping to ${m}\xD7${v}`)}(n.x!==m||n.y!==v)&&(this.log(`\u{1F4D0} Resizing display from ${n.x}\xD7${n.y} to ${m}\xD7${v} (available: ${h.width}\xD7${h.height}px, cell: ${l}\xD7${c}px)`),r.setSize(new U.Vector2(m,v)))}let u=r.getSize();(o!==u.x||d!==u.y)&&(this.log(`\u{1F4D0} Resizing renderer from ${o}\xD7${d} to ${u.x}\xD7${u.y}`),s.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.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(e,i){let r=this.rendererManager.getRenderer();if(r&&"setScalingMode"in r){let n=(0,U.valueToScalingMode)(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 o=0;o<256;o++){let d=r.get(o);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 s=this.rendererManager.getRenderer();s&&"setPalette"in s&&(s.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,s=this.localBridgeHandlers.get(r);if(s)for(let o of s)try{o(n)}catch(d){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,d)}for(let o of this.localBridgeWildcardHandlers)try{o(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}`)}};b(j,"ClientRuntime");var D=j;var M=require("@utsp/input");var N=class N{constructor(t={}){a(this,"input");a(this,"mobileVibration");this.input=new M.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 M.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=M.InputCollector.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(c.x,c.y,c.over),t.updateMacroMouse(c.x,c.y,c.isLeftDown),M.InputCollector.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let s=M.InputCollector.collectTextInputs(this.input);s.length>0&&t.setTextInputs(s);let o=t.getInputBindingRegistry(),d=o.getAllAxes(),l=o.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=o.evaluateAxis(u.bindingId,c);t.setAxis(u.name,h)}),l.forEach(u=>{let h=new Map,g=new Map,m=new Map;for(let[f,C]of p)h.set(f,C.pressed),g.set(f,C.justPressed),m.set(f,C.justReleased);let v=o.evaluateButton(u.bindingId,h),I=o.evaluateButton(u.bindingId,g),R=o.evaluateButton(u.bindingId,m);t.setButton(u.name,v),t.setButton(`${u.name}_justPressed`,I),t.setButton(`${u.name}_justReleased`,R)})}this.input.poll?.()}processVibrationCommands(t){t.hasMobileVibrationCommands()&&t.applyMobileVibrationCommands(t.flushMobileVibrationCommands()),t.hasGamepadVibrationCommands()&&t.applyGamepadVibrationCommands(t.flushGamepadVibrationCommands())}};b(N,"InputFeature");var G=N;var de=require("@utsp/audio");var J=class J{constructor(t={}){a(this,"audioManager");a(this,"debug");this.debug=t.debug??!1,this.audioManager=new de.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}`)}};b(J,"AudioFeature");var q=J;var w=require("@utsp/core"),le=require("@utsp/render"),ce=require("@utsp/types");var X=class X{constructor(t,e={}){a(this,"overlay");a(this,"orderCollector");a(this,"debug");this.debug=e.debug??!1,this.overlay=new le.PostProcessOverlay(t),this.orderCollector=new w.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 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(e,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(e,{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(e,r,i.mode);break}case w.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,ce.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}`)}};b(X,"PostProcessFeature");var W=X;var he=require("@utsp/render"),ue=require("@utsp/audio");
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");
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export { ScalingMode } from '@utsp/render';
6
6
  import { AudioManager } from '@utsp/audio';
7
7
  export { AudioManager, AudioManagerOptions, ToneOptions } from '@utsp/audio';
8
8
  import * as _utsp_input from '@utsp/input';
9
- import { UnifiedInputRouter, MobileVibration } from '@utsp/input';
9
+ import { TouchZoneDefinition, UnifiedInputRouter, MobileVibration } from '@utsp/input';
10
10
 
11
11
  /**
12
12
  * Base Runtime Types
@@ -798,6 +798,24 @@ interface BaseClientRuntimeOptions {
798
798
  /** Maximum simultaneous touches. Default: 10 */
799
799
  maxTouches?: number;
800
800
  };
801
+ /** Virtual touch zones configuration (maps screen regions to logical buttons/axes) */
802
+ touchZones?: {
803
+ /** Enable the touch zone virtual device */
804
+ enable?: boolean;
805
+ /** Grid width in cells (default: runtime display width) */
806
+ gridWidth?: number;
807
+ /** Grid height in cells (default: runtime display height) */
808
+ gridHeight?: number;
809
+ /** Renderer offsets when content is letterboxed/pillarboxed */
810
+ rendererOffsets?: {
811
+ offsetX: number;
812
+ offsetY: number;
813
+ };
814
+ /** Optional target element (canvas). If omitted, the runtime canvas is used. */
815
+ targetElement?: HTMLElement;
816
+ /** Initial zones (0-31) with cell-space rectangles */
817
+ zones?: TouchZoneDefinition[];
818
+ };
801
819
  /** Enable ImageData rendering for Canvas 2D (default: true). Provides pixel-perfect rendering with no gaps between cells. 10-20× faster than fillRect. Only for Terminal2D renderer with bitmap fonts. */
802
820
  useImageDataRendering?: boolean;
803
821
  /** Render mode: 'continuous' (default) renders at max FPS, 'on-demand' only renders when explicitly requested. */
@@ -949,6 +967,42 @@ declare class ClientRuntime extends BaseClientRuntime {
949
967
  */
950
968
  protected onStop(): Promise<void>;
951
969
  getStats(): ClientRuntimeStats;
970
+ /**
971
+ * Get audio loading state (for loading progress bars)
972
+ *
973
+ * In connected mode, tracks sounds received from server.
974
+ * In local mode, returns complete state since sounds are loaded synchronously.
975
+ *
976
+ * @returns Audio loading state
977
+ *
978
+ * @example
979
+ * ```typescript
980
+ * updateUser(core: Core, user: User): void {
981
+ * const audioState = runtime.getAudioLoadingState();
982
+ *
983
+ * if (!audioState.isComplete) {
984
+ * // Show loading bar
985
+ * const progress = audioState.loadedCount / audioState.totalExpected;
986
+ * user.renderLoadingBar(progress);
987
+ * } else {
988
+ * // Game ready
989
+ * user.renderGame();
990
+ * }
991
+ * }
992
+ * ```
993
+ */
994
+ getAudioLoadingState(): {
995
+ /** Number of sounds successfully loaded */
996
+ loadedCount: number;
997
+ /** Total number of sounds expected (0 if unknown) */
998
+ totalExpected: number;
999
+ /** Number of sounds that failed to load */
1000
+ errorCount: number;
1001
+ /** Whether all expected sounds are loaded */
1002
+ isComplete: boolean;
1003
+ /** Names of sounds that failed to load */
1004
+ errors: string[];
1005
+ };
952
1006
  /**
953
1007
  * Override onDestroy for full cleanup
954
1008
  */
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var _=Object.defineProperty;var ie=(R,t,e)=>t in R?_(R,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):R[t]=e;var y=(R,t)=>_(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 se,Terminal2D as oe}from"@utsp/render";var Z=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(Z||{});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 s=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(s,e)},"check");s()})}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 B=L;var V=class V{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 B(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 oe(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 se(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(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},d)=>{let l=this.core.getUser(d);l&&(s&&l.recordBytesReceived(s.length),o&&l.recordBytesReceived(o.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 s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let o=0,d=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:h})=>{let g=(u?.length??0)+(h?.length??0);n.recordBytesReceived(g)}),d+=performance.now()-l,this.accumulatedTime-=s,o++}if(o>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 s=0;s<256;s++){let o=e.getGlyph(s);o&&(i.set(s,o),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(V,"BaseClientRuntime");var w=V;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 P,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 $=class ${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",[]);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?.(),J={offsetX:N?.offsetX??0,offsetY:N?.offsetY??0},T=k.collectMousePosition(t,i,Y,j,J);e.setMousePosition(T.x,T.y,T.over),e.updateMacroMouse(T.x,T.y,T.isLeftDown);let X=k.collectTouchPositions(t,i,Y,j,10,J);X.forEach(U=>{e.setTouchPosition(U.id,U.x,U.y,U.over)}),this.lastCollectedTouches=X}else this.lastCollectedTouches=[];let r=e.getInputBindingRegistry(),n=r.getAllAxes(),s=r.getAllButtons(),o=k.collectAxisSources(n,t),d=k.collectButtonSources(s,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,o);p.set(f.bindingId,v),e.setAxis(f.name,v)}let u=s.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 g=e.getMouseDisplayInfo(),m=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,g?.displayId??0,g?.localX??0,g?.localY??0,m,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}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=>{this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)?this.log("Input bindings configured"):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){this.postProcessCallback&&s.postProcessOrders&&s.postProcessOrders.length>0&&this.postProcessCallback(s.postProcessOrders),this.options.debug&&this.debugRenderState();let o=performance.now();this.rendererManager.render(this.core,this.userId);let d=performance.now()-o;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 s=r.getOrders(),o=r.getStatic();(s.length>0||o)&&console.warn(` Layer ${n}: ${s.length} orders, static=${o}, 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+r.data.length+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,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}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),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{await e.loadFromData(i.soundId,i.name,i.data),this.log(`Loaded sound: ${i.name} (${i.data.length} bytes)`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}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.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,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($,"NetworkSync");var x=$;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,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,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: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.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 x(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){this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application);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){this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(n=>{this.applyPostProcessOrders(n)}),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,d=s.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!==o||p!==d)&&(this.log(`Adjusting renderer from ${c}\xD7${p} to match display ${o}\xD7${d}`),l.resize(o,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}}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 s=i.getInputBindingsLoadPacket();s&&i.recordBytesReceived(s.length),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),s=0;for(let o of r)try{if(o.loadType==="file"&&o.data){await e.loadFromData(o.soundId,o.name,o.data);let d=o.data.length+o.name.length+10;s+=d,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 d=o.url.length+o.name.length+10;s+=d,this.log(`\u{1F50A} Loaded external sound: ${o.name} from ${o.url}`)}}catch(d){console.error(`[ClientRuntime] Failed to load sound "${o.name}":`,d)}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 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 o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let d=0,l=0;for(;this.accumulatedTime>=o&&d<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 h=s.flushPostProcessCommands(),g=this.postProcessOrderCollector.convertCommands(h);this.applyPostProcessOrders(g)}if(s.hasPendingMacroOrders()){let h=s.flushMacroOrders();s.applyMacroOrders(h)}let p=s.updateMacros();if(s.processMacroEvents(p),s.hasBridgeMessages()){let h=s.getBridgeMessages();this.dispatchLocalBridgeMessages(h)}this.core.endTick().forEach(({static:h,dynamic:g})=>{let m=(h?.length??0)+(g?.length??0);s.recordBytesReceived(m)}),l+=performance.now()-c,this.accumulatedTime-=o,d++}if(d>0){let c=performance.now();this.render();let p=performance.now()-c;this.performanceMonitor.recordFrameTiming(l,p)}}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 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?.(),g={offsetX:h?.offsetX??0,offsetY:h?.offsetY??0},m=P.collectMousePosition(this.input,i,c,p,g);e.setMousePosition(m.x,m.y,m.over),e.updateMacroMouse(m.x,m.y,m.isLeftDown),P.collectTouchPositions(this.input,i,c,p,10,g).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=P.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 d=P.collectAxisSources(s,this.input),l=P.collectButtonSourcesWithTransitions(o,this.input);s.forEach(c=>{let p=n.evaluateAxis(c.bindingId,d);e.setAxis(c.name,p)}),o.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 g=n.evaluateButton(c.bindingId,p),m=n.evaluateButton(c.bindingId,u),b=n.evaluateButton(c.bindingId,h);e.setButton(c.name,g),e.setButton(`${c.name}_justPressed`,m),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(),s=this.rendererManager.getRenderer();if(s){let o,d,l,c,p=null;if(this.rendererType==="webgl"){let h=s,g=h.getGridSize();o=g.cols,d=g.rows,l=h.getCellWidth(),c=h.getCellHeight(),p=h.getScalingMode()}else{let h=s;o=h.getCols(),d=h.getRows(),l=h.getCellWidth(),c=h.getCellHeight(),p=h.getScalingMode()}if(p===he.Responsive){let h=this.rendererManager.getAvailableSize(),g=256*256;"getMaxCells"in s&&(g=Math.min(g,s.getMaxCells()));let m=Math.floor(h.width/l),b=Math.floor(h.height/c);if(m=Math.min(256,Math.max(1,m)),b=Math.min(256,Math.max(1,b)),m*b>g){let C=Math.sqrt(g/(m*b));m=Math.max(1,Math.floor(m*C)),b=Math.max(1,Math.floor(b*C)),this.log(`\u26A0\uFE0F Device limit: max ${g} cells, clamping to ${m}\xD7${b}`)}(n.x!==m||n.y!==b)&&(this.log(`\u{1F4D0} Resizing display from ${n.x}\xD7${n.y} to ${m}\xD7${b} (available: ${h.width}\xD7${h.height}px, cell: ${l}\xD7${c}px)`),r.setSize(new pe(m,b)))}let u=r.getSize();(o!==u.x||d!==u.y)&&(this.log(`\u{1F4D0} Resizing renderer from ${o}\xD7${d} to ${u.x}\xD7${u.y}`),s.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 o=0;o<256;o++){let d=r.get(o);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 s=this.rendererManager.getRenderer();s&&"setPalette"in s&&(s.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,s=this.localBridgeHandlers.get(r);if(s)for(let o of s)try{o(n)}catch(d){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,d)}for(let o of this.localBridgeWildcardHandlers)try{o(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 D=G;import{UnifiedInputRouter as be,InputCollector as A,MobileVibration as ve}from"@utsp/input";var W=class W{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=A.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(c.x,c.y,c.over),t.updateMacroMouse(c.x,c.y,c.isLeftDown),A.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let s=A.collectTextInputs(this.input);s.length>0&&t.setTextInputs(s);let o=t.getInputBindingRegistry(),d=o.getAllAxes(),l=o.getAllButtons();if(d.length>0||l.length>0){let c=A.collectAxisSources(d,this.input),p=A.collectButtonSourcesWithTransitions(l,this.input);d.forEach(u=>{let h=o.evaluateAxis(u.bindingId,c);t.setAxis(u.name,h)}),l.forEach(u=>{let h=new Map,g=new Map,m=new Map;for(let[f,v]of p)h.set(f,v.pressed),g.set(f,v.justPressed),m.set(f,v.justReleased);let b=o.evaluateButton(u.bindingId,h),C=o.evaluateButton(u.bindingId,g),I=o.evaluateButton(u.bindingId,m);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(W,"InputFeature");var q=W;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(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}`)}};y(H,"AudioFeature");var Q=H;import{PostProcessOrderCollector as Ie,PostProcessOrderType as F}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 F.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 F.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 F.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 F.SetScalingMode:{this.applyScalingMode(e,r,i.mode);break}case F.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 z=K;import{ScalingMode as St}from"@utsp/render";import{AudioManager as Mt}from"@utsp/audio";export{Q as AudioFeature,Mt as AudioManager,w as BaseClientRuntime,D as ClientRuntime,q as InputFeature,z as PostProcessFeature,Z as RendererType,St as ScalingMode};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utsp/runtime-client",
3
- "version": "0.14.1",
3
+ "version": "0.15.0-nightly.20251227200128.cdc09ed",
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.14.1",
52
- "@utsp/core": "0.14.1",
53
- "@utsp/input": "0.14.1",
54
- "@utsp/render": "0.14.1",
55
- "@utsp/network-client": "0.14.1",
56
- "@utsp/types": "0.14.1"
51
+ "@utsp/audio": "0.15.0-nightly.20251227200128.cdc09ed",
52
+ "@utsp/core": "0.15.0-nightly.20251227200128.cdc09ed",
53
+ "@utsp/input": "0.15.0-nightly.20251227200128.cdc09ed",
54
+ "@utsp/network-client": "0.15.0-nightly.20251227200128.cdc09ed",
55
+ "@utsp/render": "0.15.0-nightly.20251227200128.cdc09ed",
56
+ "@utsp/types": "0.15.0-nightly.20251227200128.cdc09ed"
57
57
  },
58
58
  "devDependencies": {
59
59
  "typescript": "^5.6.3"