@utsp/runtime-server
Version:
Server-side runtime for UTSP applications (Node.js, headless)
2 lines (1 loc) • 6.77 kB
JavaScript
"use strict";var h=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var f=(r,t,i)=>t in r?h(r,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):r[t]=i;var c=(r,t)=>h(r,"name",{value:t,configurable:!0});var w=(r,t)=>{for(var i in t)h(r,i,{get:t[i],enumerable:!0})},T=(r,t,i,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of k(t))!v.call(r,e)&&e!==i&&h(r,e,{get:()=>t[e],enumerable:!(n=m(t,e))||n.enumerable});return r};var C=r=>T(h({},"__esModule",{value:!0}),r);var a=(r,t,i)=>(f(r,typeof t!="symbol"?t+"":t,i),i);var S={};w(S,{ServerRuntime:()=>p});module.exports=C(S);var d=require("@utsp/core"),g=require("@utsp/network-server");var l=class l{constructor(t){a(this,"core");a(this,"network");a(this,"options");a(this,"running",!1);a(this,"startTime",0);a(this,"lastTimestamp",0);a(this,"tickCount",0);a(this,"tps",0);a(this,"tpsUpdateTime",0);a(this,"tickInterval",null);this.options={application:t.application,port:t.port,host:t.host??"0.0.0.0",tickRate:t.tickRate??20,maxConnections:t.maxConnections??100,debug:t.debug??!1,width:t.width??80,height:t.height??25,cors:t.cors??{origin:"*"}},this.log("Initializing ServerRuntime..."),this.core=new d.Core({mode:"server",maxUsers:this.options.maxConnections}),this.network=new g.SocketIOServer({port:this.options.port,host:this.options.host,cors:this.options.cors,maxConnections:this.options.maxConnections,debug:this.options.debug}),this.log("ServerRuntime initialized")}getMode(){return"server"}isRunning(){return this.running}setTickRate(t){if(t<=0||t>1e3)throw new Error(`Invalid tick rate: ${t}. Must be between 1 and 1000.`);if(this.options.tickRate=t,this.log(`Tick rate changed to ${t} TPS`),this.running&&this.tickInterval){clearInterval(this.tickInterval);let i=1e3/t;this.log(`Restarting tick loop at ${t} TPS (${i}ms)...`),this.tickInterval=setInterval(()=>{this.tick()},i)}}getTickRate(){return this.options.tickRate}async start(){if(this.running){this.log("Already running");return}this.log("Starting ServerRuntime..."),await this.network.start(),this.log(`Server listening on ${this.options.host}:${this.options.port}`),this.setupNetworkHandlers(),this.log("Calling application.init()..."),await this.options.application.init(this.core,this),this.running=!0,this.startTime=Date.now(),this.lastTimestamp=this.startTime,this.tpsUpdateTime=this.startTime;let t=1e3/this.options.tickRate;this.log(`Starting tick loop at ${this.options.tickRate} TPS (${t}ms)...`),this.tickInterval=setInterval(()=>{this.tick()},t)}async stop(){if(!this.running)return;this.log("Stopping ServerRuntime..."),this.running=!1,this.tickInterval&&(clearInterval(this.tickInterval),this.tickInterval=null),this.network.getClients().forEach(i=>{this.network.disconnectClient(i,"Server stopped")}),await this.network.stop(),this.log("ServerRuntime stopped")}getStats(){return{mode:"server",running:this.running,userCount:this.core.getUsers().length,tps:this.tps,uptime:this.running?Date.now()-this.startTime:0,totalTicks:this.tickCount}}async destroy(){await this.stop(),this.log("Destroying ServerRuntime..."),this.options.application.destroy&&this.options.application.destroy(),await this.network.destroy(),this.log("ServerRuntime destroyed")}setupNetworkHandlers(){this.network.onConnect(t=>{this.log(`Client connected: ${t}`)}),this.network.onDisconnect((t,i)=>{this.log(`Client disconnected: ${t} (${i})`);let n=this.core.getUser(t);n&&(this.options.application.destroyUser&&this.options.application.destroyUser(this.core,n,i),this.core.removeUser(t))}),this.network.on("join",(t,i)=>{this.log(`Join request from ${t}: ${i.username}`);try{let n=i.username||`Player${t.substring(0,4)}`,e=this.core.createUser(t,n);this.options.application.initUser(this.core,e,{username:n,token:i.token}),this.network.sendToClient(t,"join_response",{success:!0,userId:t,roomId:"main"});let o=this.core.generateAllLoadPackets();this.log(`Sending ${o.length} load packets to ${t}...`),o.forEach(u=>{this.network.sendToClient(t,"load",u)});let s=e.getInputBindingsLoadPacket();s&&(this.network.sendToClient(t,"input-bindings",s),this.log(`Sent input bindings to ${t}`)),this.log(`User ${n} (${t}) joined`)}catch(n){this.log(`Failed to join: ${n}`),this.network.sendToClient(t,"join_response",{success:!1,error:"Failed to join game"})}}),this.network.on("leave",t=>{this.log(`Leave request from ${t}`),this.network.disconnectClient(t,"User left")}),this.network.on("input",(t,i)=>{let n=this.core.getUser(t);if(!n){this.log(`Input from unknown user: ${t}`);return}if(i.axes&&Object.entries(i.axes).forEach(([e,o])=>{n.setAxis(e,o)}),i.buttons&&Object.entries(i.buttons).forEach(([e,o])=>{n.setButton(e,o)}),i.mousePosition){let{x:e,y:o}=i.mousePosition;n.setMousePosition(e,o,!0)}i.textInputs&&Array.isArray(i.textInputs)&&n.setTextInputs(i.textInputs)})}tick(){if(!this.running)return;let t=Date.now(),i=(t-this.lastTimestamp)/1e3;this.lastTimestamp=t,this.tickCount++,t-this.tpsUpdateTime>=1e3&&(this.tps=Math.round(this.tickCount*1e3/(t-this.tpsUpdateTime)),this.tickCount=0,this.tpsUpdateTime=t),this.options.application.update&&this.options.application.update(this.core,i),this.core.getUsers().forEach(e=>{if(this.options.application.updateUser(this.core,e,i),e.clearTextInputs(),e.needsSendSounds()){let o=this.core.generateSoundLoadPackets();o.length>0&&(this.log(`Sending ${o.length} sound packets to ${e.id}...`),o.forEach(s=>{this.network.sendToClient(e.id,"sound-load",s)})),e.clearSendSounds()}e.hasAudioConfigCommands()&&e.flushAudioConfigCommands().forEach(s=>{this.network.sendToClient(e.id,"audio-config",s)}),e.hasSoundCommands()&&e.flushSoundCommands().forEach(s=>{if("fadeOut"in s&&s.fadeOut===!0){this.network.sendToClient(e.id,"sound-fadeout",s);return}if("pause"in s&&s.pause===!0){this.network.sendToClient(e.id,"sound-pause",s);return}if("resume"in s&&s.resume===!0){this.network.sendToClient(e.id,"sound-resume",s);return}"instanceId"in s||"volume"in s||"pitch"in s||"loop"in s||"fadeIn"in s||"position"in s?this.network.sendToClient(e.id,"sound-play",s):this.network.sendToClient(e.id,"sound-stop",s)})}),this.core.endTick(),this.broadcastUpdates()}broadcastUpdates(){let t=this.core.endTickSplit(),i=this.core.getCurrentTick();t.forEach(({static:n,dynamic:e},o)=>{if(n&&(this.network.sendToClient(o,"update-static",n),console.warn(`[SERVER] update-static: ${n.length}B (tick ${i})`)),e){let s=this.network;s.sendToClientVolatile?s.sendToClientVolatile(o,"update-dynamic",e):this.network.sendToClient(o,"update-dynamic",e),console.warn(`[SERVER] update-dynamic: ${e.length}B (tick ${i})`)}})}log(t){this.options.debug&&console.warn(`[ServerRuntime] ${t}`)}};c(l,"ServerRuntime");var p=l;0&&(module.exports={ServerRuntime});