diff --git a/app/entry.server.jsx b/app/entry.server.jsx index 5c43a41..3639023 100644 --- a/app/entry.server.jsx +++ b/app/entry.server.jsx @@ -13,15 +13,9 @@ import { renderToPipeableStream } from "react-dom/server"; const ABORT_DELAY = 5_000; -export async function websocket(server, viteDevServer) { - if (viteDevServer) { - const {createViteRuntime} = await import('vite'); - const runtime = await createViteRuntime(viteDevServer); - (await runtime.executeEntrypoint('/app/server/websocket.js')).default(server); - } - else { - (await import('./server/websocket.js')).default(server); - } +export async function handleUpgrade(request, socket, head) { + const {handleUpgrade} = await import('./server/websocket.js'); + handleUpgrade(request, socket, head); } export default function handleRequest( diff --git a/app/server/engine.js b/app/server/engine.js index 977d486..3a382e7 100644 --- a/app/server/engine.js +++ b/app/server/engine.js @@ -449,7 +449,13 @@ export default class Engine { loop(); } - stop() { + async stop() { + const promises = []; + for (const [connection] of this.connectedPlayers) { + promises.push(this.disconnectPlayer(connection)); + } + await Promise.all(promises); + await this.saveEcses(); clearTimeout(this.handle); this.handle = undefined; } diff --git a/app/server/websocket.js b/app/server/websocket.js index 92077c2..4dfacf3 100644 --- a/app/server/websocket.js +++ b/app/server/websocket.js @@ -10,38 +10,7 @@ import Engine from './engine.js'; const isInsecure = process.env.SILPHIUS_INSECURE_HTTP; -const wss = new WebSocketServer({ - noServer: true, -}); - -function onUpgrade(request, socket, head) { - const {pathname} = new URL(request.url, 'wss://base.url'); - if (pathname === '/ws') { - wss.handleUpgrade(request, socket, head, function done(ws) { - wss.emit('connection', ws, request); - }); - } - else { - socket.destroy(); - } -} - -let engine; -let onConnect; - -function createOnConnect(engine) { - onConnect = async (ws, request) => { - ws.on('close', async () => { - await engine.disconnectPlayer(ws); - }) - ws.on('message', (packed) => { - engine.server.accept(ws, new DataView(packed.buffer, packed.byteOffset, packed.length)); - }); - const session = await getSession(request.headers['cookie']); - await engine.connectPlayer(ws, session.get('id')); - }; - wss.on('connection', onConnect); -} +global.__silphiusWebsocket = null; class SocketServer extends Server { async ensurePath(path) { @@ -75,35 +44,55 @@ class SocketServer extends Server { transmit(ws, packed) { ws.send(packed); } } -async function createEngine(Engine) { - engine = new Engine(SocketServer); - await engine.load(); - engine.start(); - return engine; -} - -async function remakeServer(Engine) { - if (onConnect) { - wss.off('connection', onConnect); +export async function handleUpgrade(request, socket, head) { + if (!global.__silphiusWebsocket) { + const engine = new Engine(SocketServer); + await engine.load(); + engine.start(); + const handleConnection = async (ws, request) => { + ws.on('close', async () => { + await engine.disconnectPlayer(ws); + }) + ws.on('message', (packed) => { + engine.server.accept(ws, new DataView(packed.buffer, packed.byteOffset, packed.length)); + }); + const session = await getSession(request.headers['cookie']); + await engine.connectPlayer(ws, session.get('id')); + }; + const wss = new WebSocketServer({ + noServer: true, + }); + wss.on('connection', handleConnection); + global.__silphiusWebsocket = {engine, handleConnection, wss}; } - if (engine) { - for (const [connection] of engine.connectedPlayers) { - connection.close(); - } + const {pathname} = new URL(request.url, 'wss://base.url'); + if (pathname === '/ws') { + const {wss} = global.__silphiusWebsocket; + wss.handleUpgrade(request, socket, head, function done(ws) { + wss.emit('connection', ws, request); + }); + } + else { + socket.destroy(); } - createOnConnect(await createEngine(Engine)); } -(async () => { - await remakeServer(Engine); -})(); - if (import.meta.hot) { - import.meta.hot.accept('./engine.js', async ({default: Engine}) => { - await remakeServer(Engine); + import.meta.hot.on('vite:beforeUpdate', async () => { + if (global.__silphiusWebsocket) { + const {engine, handleConnection, wss} = global.__silphiusWebsocket; + wss.off('connection', handleConnection); + const connections = []; + for (const [connection] of engine.connectedPlayers) { + engine.server.send(connection, {type: 'EcsChange'}); + connections.push(connection); + } + await engine.stop(); + for (const connection of connections) { + connection.close(); + } + global.__silphiusWebsocket = null; + } }); -} - -export default async function listen(server) { - server.on('upgrade', onUpgrade); + import.meta.hot.accept(); } diff --git a/server.js b/server.js index 3605eab..c0846da 100644 --- a/server.js +++ b/server.js @@ -61,11 +61,23 @@ const build = () => ( ? viteDevServer.ssrLoadModule('virtual:remix/server-build') : import('./build/server/index.js') ); -const ssr = await build(); -await ssr.entry.module.websocket(server, viteDevServer); -const remixHandler = createRequestHandler({ - build: () => ssr, -}); +let remixHandler; +if (viteDevServer) { + const {createViteRuntime} = await import('vite'); + const runtime = await createViteRuntime(viteDevServer); + const {handleUpgrade} = await runtime.executeEntrypoint('/app/server/websocket.js'); + server.on('upgrade', handleUpgrade); + remixHandler = createRequestHandler({build}); +} +else { + const ssr = await build(); + server.on('upgrade', ssr.entry.module.handleUpgrade); + remixHandler = createRequestHandler({ + build: async () => { + return ssr; + }, + }); +} // configure middleware app.use(compression());