@utsp/runtime-server
Version:
Server-side runtime for UTSP applications (Node.js, headless)
2 lines (1 loc) • 6.31 kB
JavaScript
var u=Object.defineProperty;var d=(a,t,i)=>t in a?u(a,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):a[t]=i;var c=(a,t)=>u(a,"name",{value:t,configurable:!0});var r=(a,t,i)=>(d(a,typeof t!="symbol"?t+"":t,i),i);import{Core as g}from"@utsp/core";import{SocketIOServer as m}from"@utsp/network-server";var p=class p{constructor(t){r(this,"core");r(this,"network");r(this,"options");r(this,"running",!1);r(this,"startTime",0);r(this,"lastTimestamp",0);r(this,"tickCount",0);r(this,"tps",0);r(this,"tpsUpdateTime",0);r(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 g({mode:"server",maxUsers:this.options.maxConnections}),this.network=new m({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(l=>{this.network.sendToClient(t,"load",l)});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(p,"ServerRuntime");var h=p;export{h as ServerRuntime};